diff -urN tiarra-20040122/AUTHORS tiarra-20040214/AUTHORS
--- tiarra-20040122/AUTHORS	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20040214/AUTHORS	2004-02-15 01:45:21.000000000 +0900
@@ -0,0 +1,46 @@
+-*- text -*-
+$Id: AUTHORS,v 1.1 2004/02/14 08:44:24 admin Exp $
+プログラム: Tiarra
+メインの開発者: phonohawk <phonohawk@ps.sakura.ne.jp>
+
+Authors (敬称略)
+===============
+
+* tiarra, main/*
+
+phonohawk: 設計, 殆どのクラスの実装
+Topia: 幾つかのクラスの設計と実装, 多数の改良とバグ修正
+
+* module/下、Auto関連
+
+Topia: ほぼ全て
+phonohawk: (初期バージョンのみ)設計と実装
+
+* module/下、Auto以外
+
+phonohawk: ほぼ全て
+Topia: 幾つかのクラスの設計と実装, 多数の改良とバグ修正
+
+* tiarra-conf.el
+
+phonohawk
+
+* tiarra-conf.l
+
+Noboruhi: tiarra-conf.elからの移植
+
+* doc/*
+
+phonohawk: ほぼ全て
+
+* アーカイブの作成と配布
+
+Topia (2004年2月現在)
+
+===============
+その他、多数の方々よりバグ報告や改良案等を頂き、Tiarraは改善されております。
+この場を借りて感謝の意を申し上げます。
+
+- phonohawk -                     http://ccm.sherry.jp/
+OpenPGP public key: 1024D/1A86EF72
+Fpr: 5F3E 5B5F 535C CE27 8254  4D1A 14E7 9CA7 1A86 EF72
diff -urN tiarra-20040122/ChangeLog tiarra-20040214/ChangeLog
--- tiarra-20040122/ChangeLog	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/ChangeLog	2004-02-15 01:45:21.000000000 +0900
@@ -1,3 +1,132 @@
+2004-02-14  Topia  <topia@clovery.jp>
+
+	* 全般:
+	  (a) NumericReply を使うようにした。
+	  (b) general/sysmsg-prefix を使うようにした。
+
+	* doc-src/conf-main.tdoc, sample.conf:
+	  - sysmsg-prefix を追加。
+
+	* sample.conf:
+	  - Client::Eval と Client::Cache を追加。
+
+	* main/ChannelInfo.pm:
+	  - mode_string method を追加。
+
+	* main/Configuration.pm:
+	  - general/sysmsg-prefix のデフォルト値を追加。
+
+	* main/IRCMessage.pm:
+	  - clone(deep => 1) を追加。
+	  - s/unvalid/invalid/; fix typo.
+	  - $this->[PARAMS] がそもそも未定義なときは、
+	    n_params は中身なし配列の長さ(=0)を返すようにした。
+
+	* main/ModuleManager.pm:
+	  - module の reload の時に、一時的に $this->{modules} の
+	    該当箇所に undef を入れて実行されないようにした。
+	    (main/RunLoop.pm の変更とセットです。)
+
+	* main/Multicast.pm: (b)
+	  - シングルサーバモードで、どこのネットワークにも
+	    繋がっていないときは、デフォルトネットワークに
+	    $networks->default を使うようにした。
+
+	* main/NumericReply.pm:
+	  - irc2.10.3p5+hemp2 に合わせた。
+	    ISUPPORT は使用する設定。
+	  - fetch_number(名前から番号), fetch_name(番号から名前)
+	    のそれぞれを得る関数を追加した。
+
+	* main/PersonInChannel.pm:
+	  - priv_symbol method (privilege symbol) を追加。
+	    has_[ov] の状態によって、 @, +, 空文字列のどれかを返す。
+
+	* main/PersonalInfo.pm:
+	  - AWAY を追加。できるだけ更新しますが、
+	    性質上情報の正確さは保証できません。
+
+	* main/RunLoop.pm: (b)
+	  - single-server-mode 時に、切断・接続のアナウンスの
+	    送信先チャンネル名からネットワーク名をはずすようにした。
+	  - サーバへの接続処理時に、 couldn't connect to 以外の
+	    エラーメッセージ表示には notify_error を使うようにした。
+	  - クライアントからの接続処理時は、エラーメッセージ表示に
+	    notify_msg を使うようにした。
+	  - apply_filters で、 modules_list の中に undef が出てきたら、
+	    それを無視するようにした。
+	    (main/ModuleManager.pm の変更も参照。)
+
+	* main/IrcIO/Client.pm: (a)(b)
+	  - credit を RPL_YOURHOST でなく MOTD として表示するようにした。
+	  - RPL_YOURHOST では、 Tiarra のバージョンを表示する。
+
+	* main/IrcIO/Server.pm: (a)
+	  - _RPL_* の処理を NumericReply::fetch_name を使ってまとめた。
+	  - _START_WHOIS_REPLY, _RPL_ENDOFWHOIS, _RPL_AWAY を追加。
+	  - _RPL_WHOREPLY
+	    + 空白を含む realname に関するバグを修正。
+	    + away 情報を記憶するようにした。
+	    + server hop 情報を network の 'server-hops' remark に保存。
+	  - _RPL_CHANNELMODEIS で、 switches と parameters の情報は
+	    この reply で得られると決めつけて、クリア処理を行う。
+
+	* module/Channel/Freeze.pm: (b)
+
+	* module/Channel/Rejoin.pm: (a)
+	  - ChannelInfo->mode_string を使用するようにした。
+
+	* module/Channel/Join/Connect.pm:
+	  - コンマの直後にスペースがあった場合、削除する処理が、
+	    最初の一つに対してしか実行されていなかったのを修正…(^^;;;
+
+	* module/Client/Cache.pm:
+	  - 追加。いまのところ MODE キャッシュと、 WHO キャッシュを実装。
+
+	* module/Client/Eval.pm:
+	  - 追加。クライアントからのコマンドしか受け付けないが、
+	    その代わりすべてのコマンドを実行できる。
+	    事実上 IRC パスワードがわかれば Tiarra が動いているホスト上で
+	    動作しているアカウントの権限で何でもできる、
+	    ということに注意すること。
+
+	* module/Log/Recent.pm: (b)
+	  - network name が不正な場合は、 notify_warn で警告して、
+	    エラーなしに抜けるようにした。
+
+	* module/System/Pong.pm:
+	  - prefix がついているのは不自然だったので、削った。
+
+	* module/System/Raw.pm: (b)
+
+	* module/Tools/LinedDB.pm:
+	  - ファイルが存在しない場合に、
+	    更新チェック部分でエラーが発生していたのを修正。
+
+2004-02-04  phonohawk  <phonohawk@ps.sakura.ne.jp>
+
+	* main/IrcIO.pm, main/IrcIO/*.pm, main/Module.pm:
+	notification_of_message_ioを削除。
+
+	* main/Module.pm (message_io_hook):
+	追加。これはnotification_of_message_ioの代わりに呼ばれる。
+	このメソッドはメッセージを改変する事が出来る。詳しくはコメントに。
+
+	* main/IrcIO.pm (send_message, receive):
+	各モジュールのmessage_io_hookを呼ぶ。
+
+	* main/RunLoop.pm (apply_filters):
+	追加。モジュールによるメッセージフィルタリングの一般形。
+
+2004-01-27  phonohawk  <phonohawk@ps.sakura.ne.jp>
+
+	* main/NumericReply.pm: 追加
+	ニューメリックリプライをシンボルとして定義するクラス。
+	useで全シンボルをエクスポート。
+
+	* main/IrcIO.pm: 
+	CRが無く、LFだけで終わっているメッセージも受け入れる。
+	
 2004-01-23  Topia  <topia@clovery.jp>
 
 	* tiarra: $0 自体が symlink だったときに、@INC に symlink 先の
@@ -5,19 +134,19 @@
 	カレントディレクトリ・$0 のディレクトリは常に含むようにした。
 	(make_password): --make-password=password を可能にした。
 
-2004-01-23  phonohawk  <phonohawk@lycos.co.uk>
+2004-01-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra: 起動時オプション --make-password 追加。
 	make-passwordの機能をtiarra本体に移した。
 	
 	* make-password: 削除
 
-2004-01-20  phonohawk  <phonohawk@lycos.co.uk>
+2004-01-20  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Unicode/Japanese.pm:
 	同梱のUniJPを0.18から0.19に。JISの問題は解決。
 
-2004-01-14  phonohawk  <phonohawk@lycos.co.uk>
+2004-01-14  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Mask.pm:
 	マスクから作った正規表現をqrでコンパイルする際、
@@ -56,7 +185,7 @@
 	PRIVMSG/NOTICE のブロードキャストで、 network-suffix 付きの
 	チャンネルに送信してしまっていた。
 
-2003-11-09  phonohawk  <phonohawk@lycos.co.uk>
+2003-11-09  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra (help):
 	メッセージ中のstdinとstderrの間違いを修正。
@@ -77,7 +206,7 @@
 	single-server-mode 時に、クライアントから送られて来るメッセージに
 	network-suffix を付けるようにした。
 
-2003-10-25  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-25  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/IRCMessage.pm (serialize):
 	最後のパラメータが空文字列だった場合、コロンを残さない為に
@@ -86,7 +215,7 @@
 	* main/IrcIO/Server.pm (_receive_while_logging_in):
 	サーバーがERRORを返した時、その内容でdieするように。
 
-2003-10-24  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-24  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Configuration/Block.pm:
 	$block->foo_bar('block') とした時、戻り値が常にブロックとなる。
@@ -106,7 +235,7 @@
 	* module/Auto/Alias.pm (message_arrived):
 	返り値がおかしかったのを修正。
 
-2003-10-19  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-19  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/IrcIO/Server.pm
 	(person_if_exists):
@@ -114,7 +243,7 @@
 	(_RPL_WHOREPLY):
 	サーバー名とnickの位置を間違えていたので修正。
 
-2003-10-16  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-16  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* module/System/Raw.pm:
 	追加。Tiarraに改変されない生のメッセージをサーバーに送るためのモジュール。
@@ -138,12 +267,12 @@
 	single-server-mode 時の NICK 処理に RunLoop/set_current_nick を追加。
 	437 での if 条件であほなミスをしていたので修正。
 
-2003-10-12  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-12  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/IRCMessage.pm (serialize):
 	最後のパラメータがコロンを含んでいる時に、間違った文字列化をする問題を解決。
 
-2003-09-28  phonohawk  <phonohawk@lycos.co.uk>
+2003-09-28  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.el:
 	mmm-modeがインストールされていて、(require 'mmm-mode)または
@@ -216,7 +345,7 @@
 	Channel::Join::Connect の ブロックを TiarraDoc から再生成。
 	指定項目の変化はありません。
 
-2003-09-24  phonohawk  <phonohawk@lycos.co.uk>
+2003-09-24  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Multicast.pm (nick_p):
 	「|」を含むnickをnickと認識していなかったので修正。
@@ -224,7 +353,7 @@
 	* module/Log/Recent.pm:
 	configのcommandを小文字で書くとログが取られない問題を解決。
 
-2003-09-23  phonohawk  <phonohawk@lycos.co.uk>
+2003-09-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/ModuleManager.pm:
 	use Module::Useされたサブモジュールが破棄される時に、
@@ -245,7 +374,7 @@
 	* module/Log/Channel.pm:
 	configのcommandを小文字で書くとログが取られない問題を解決。
 
-2003-09-20  phonohawk  <phonohawk@lycos.co.uk>
+2003-09-20  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/ChannelInfo.pm, main/IRCMessage.pm,
 	main/PersonInChannel.pm, main/PersonalInfo.pm,
@@ -279,7 +408,7 @@
 	アドレスコピーなどの機能が使えない症状が無くなった。
 	$client_sent の numeric reply にコメントを補完。
 
-2003-08-12  phonohawk  <phonohawk@lycos.co.uk>
+2003-08-12  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/FunctionalVariable.pm:
 	追加。与えられた任意のハンドラを変数にtieする。
@@ -305,7 +434,7 @@
 	_inform_joinning_channelsをプライベートメソッドでなくした。
 	新しいメソッド名はinform_joinning_channels。
 
-2003-08-04  phonohawk  <phonohawk@lycos.co.uk>
+2003-08-04  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* makedoc:
 	追加。このスクリプトはdoc-src下のファイルとmodule下のモジュール、
@@ -400,12 +529,12 @@
 	* sample.conf:
 	mask 関連を修正。
 
-2003-07-28  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-28  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/IrcIO/Server.pm (person_list):
 	追加。覚えている全てのPersonalInfoのリストを返す。
 
-2003-07-26  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-26  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra:
 	起動時に`-Dfoo'や`-Dfoo=bar'を指定すると、confに`@define foo'や
@@ -427,7 +556,7 @@
 	* main/Multicast.pm:
 	352(WHOREPLY)のチャンネル名にネットワーク名をアタッチするようにした。
 
-2003-07-23  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/InstantCapsule.pm:
 	SelfLoader使用中止。SelfLoaderでDESTROYが定義されると
@@ -437,7 +566,7 @@
 	コマンド「/VANISHDEBUG 1」でメッセージの改変される様子が見えるように。
 	現在残っている妙な不具合の原因が解り次第削除。
 
-2003-07-22  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-22  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* module/Channel/Freeze.pm
 	(freeze):
@@ -447,13 +576,13 @@
 	凍結していないチャンネルをdefrostするとエラーが起こる問題を解決。
 	チャンネル名をマスクとして扱うように変更。
 
-2003-07-20  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-20  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/IrcIO/Server.pm (_PART):
 	PART受信時、入っているどのチャンネルにも最早その人物が
 	居なくなった場合は、その人物についてのPersonalInfoを削除する。
 
-2003-07-19  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-19  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/ChannelInfo.pm (remark):
 	remarks()のエイリアスとしてremark()を使用可能に。
@@ -465,13 +594,13 @@
 	* main/IrcIO/Server.pm (_TOPIC): 
 	TOPICメッセージを受信した時、古いトピックを'old-topic'として註釈を付ける。
 
-2003-07-17  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-17  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.el (tiarra-conf-jump-to-block): 
 	ブロック名を入力し、その位置へジャンプするコマンド。
 	デフォルトでは C-c C-. 及び C-c . に割当てられている。
 
-2003-07-16  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-16  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.el (tiarra-conf-next-token):
 	追加。カレントバッファの現在のカーソル位置の次にあるトークンを返す。
@@ -484,14 +613,14 @@
 	プリプロセッサ指令があると変な動作をするバグ有り。
 	nextは M-n に、prevは M-p に割当てた。
 
-2003-07-10  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-10  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* module/Channel/Freeze.pm:
 	追加。特定のチャンネルのNOTICEやPRIVMSGの中継を
 	一時的に中断するためのモジュール。
 	発言を見たくないがPARTはしたくない、といった場合に有効。
 
-2003-07-03  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-03  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Configuration/Block.pm (get):
 	$config->foo('random')のような呼出しを可能に。
@@ -508,19 +637,19 @@
 	作成されたクロージャに、発言内容としてundefを渡した場合、
 	何もせずに処理を終える。
 
-2003-06-21  phonohawk  <phonohawk@lycos.co.uk>
+2003-06-21  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra:
 	ActivePerlで起動時に出ていた警告を出ないように変更。
 	SIGHUPのハンドラをインストールする際の警告だった。
 
-2003-06-19  phonohawk  <phonohawk@lycos.co.uk>
+2003-06-19  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Multicast.pm (attach,detach) :
 	チャンネル/nickとネットワーク名の区切り文字として、二文字以上の文字列も使用可能に。
 	つまり、今後は区切り文字として「空白を含まない1文字以上の任意の文字列」を使う事が出来ます。
 
-2003-06-06  phonohawk  <phonohawk@lycos.co.uk>
+2003-06-06  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* PersonInChannel.pm (remark) :
 	明示的に二番目の引数にundefを渡すと、その註釈が削除される。
@@ -548,7 +677,7 @@
 	* IrcIO.pm (send) : 接続チェックの対象に $this->{sock}->connected も追加。
 	これが接続されてない状態で書き込もうとすると perl 自体がエラー落ちすることがあるらしい。
 
-2003-06-04  phonohawk  <phonohawk@lycos.co.uk>
+2003-06-04  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra (ipv6_enabled) : IPv6が有効かどうかを真偽値で返す。
 
@@ -559,7 +688,7 @@
 
 	* ChannelInfo.pm, PersonalInfo.pm: DESTROY時にエラーが起こる問題を解決。
 
-2003-05-27  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-27  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* RunLoop.pm:
 	(notify_warn): 追加。全クライアントとコンソールに警告文を出力する。
@@ -571,7 +700,7 @@
 	* ExternalSocket.pm:
 	WantToWriteが返した真偽値によらず、常に「書き込みが必要」として処理していた問題を解決。
 
-2003-05-26  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-26  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Timer.pm (new):
 	AtとAfter(又はInterval)が、両方とも指定されていなければcroakする。
@@ -581,7 +710,7 @@
 	%PREの評価結果がundefになった時に警告が出ていたのを修正。
 	@if文や@elsif文の評価結果がエラーになった時、そのエラー内容を表示していなかったので修正。
 
-2003-05-24  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-24  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IO/Socket/INET6.pm: 追加。
 	IO::Socket::INETをIPv6に移植。Socket6.pmが必要。
@@ -609,7 +738,7 @@
 	  無視する行を指定するクロージャを引数に取れるようになった。省略された場合は # で始まる行を無視する。(従来)
 	  del_value は削除出来た値の数を返すようになった。また、 value が未指定ならキーごと削除する。
 
-2003-05-21  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-21  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.l: 追加。
 	  Noboruhiさんによるxyzzy用tiarra.conf編集モード。
@@ -621,7 +750,7 @@
 	  NOTICE/PRIVMSG の処理は面倒なので、これを自動的に処理する。
 	  sendto_channel_closure, generate_reply_closures に使用方法のコメントを追加。
 	
-2003-05-15  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-15  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* conf:
 	  general/control-socket-nameを定義すると、外部プログラムからtiarraをコントロールする為の
@@ -641,7 +770,7 @@
 
 	* Log/Channel.pm: "ID: synchronize"で外部からのログの同期を可能に。
 
-2003-04-29  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-29  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IrcIO/Server.pm: ログイン時、サーバーから送られてきたニューメリックリプライ以外のメッセージを無視する。
 	  ログイン前にNOTICEを送るようなサーバーに繋げられない問題を解決。
@@ -655,7 +784,7 @@
 
 	* RunLoop.pm: サーバーへの再接続時、+kされたチャンネルへの再JOINに失敗していたのを修正。
 
-2003-04-25  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-25  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* main/Configuration.pm: general/client-allowedが省略された場合、
 	  間違った値をデフォルト値として設定していたので修正。
@@ -666,7 +795,7 @@
 	* Configuration.pm: networksのnameで列挙されたネットワーク名に対応する
 	  ブロックの定義が無かった場合、適切なエラーメッセージを出さずに処理を止めていたので修正。
 
-2003-04-18  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-18  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra: SIGHUPを受信した時の動作を変更。
 	  これまではシャットダウンしていたが、以後は設定をリロードする。
@@ -683,7 +812,7 @@
 	  それぞれIrcIO::Serverであれば1を返すメソッドと
 	  IrcIO::Clientであれば1を返すメソッド。
 	
-2003-04-13  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-13  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* User/Vanish.pm: 追加
 	  特定のチャンネルでの特定の人物の存在をクライアントに隠すモジュール。
@@ -695,7 +824,7 @@
 
 	* ChannelInfo.pm (AUTOLOAD): ハッシュマップの操作コマンドとして'keys'と'values'を追加。
 
-2003-04-10  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-10  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* RunLoop.pm: 切断に気付かない場合があるので、3分毎に各サーバーにPINGを発行する。
 	  PING自動発行後にサーバーから来た最初のPONGは破棄される。
@@ -705,7 +834,7 @@
 	* LinedINETSocket.pm: メソッドconnectの動作をconnectとattachの二つに分けた。
 	  これにより予め開かれたIO::Socket::INETに対してLinedINETSocketの機能を適用可能。
 	
-2003-04-05  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-05  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Auto/Joined.pm: 追加。
 	  特定のチャンネルに誰かがJOINする度に特定の発言を行なうモジュール。
@@ -722,7 +851,7 @@
 	* Mask.pm: add array_or_(all|all_chan), (all|all_chan)_mask.
 	  for not known maskmode, use Tiarra mode; and do warn.
 
-2003-03-23  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IrcIO/Server.pm: general/bind-addrでサーバーへの接続時のローカルアドレスを指定可能に。
 	  また、各ネットワーク設定でbind-addrはオーバーライドできる。
@@ -735,7 +864,7 @@
 
 	* tiarra: ChangeLogからDateとRevisionを読んでバージョン情報に付加するようにした。
 
-2003-03-23  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* User/Filter.pm: 追加。特定のユーザーの発言にフィルタをかけるモジュール。
 
@@ -804,7 +933,7 @@
 	  add #(file_lines:fpath:mode:charset).
 	(register_callback): $reg_callback accept scalar function name.
 
-2003-03-15  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-15  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* BulletinBoard.pm:
 	  ・AUTOLOAD経由で値を設定可能に。
@@ -820,7 +949,7 @@
 	  CTCP/UserInfo.pm,
 	  CTCP/Version.pm     : 追加。
 
-2003-03-10  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-10  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IrcIO/Server.pm: ネットワーク毎の設定でin-encoding,out-encodingを定義する事により
 	  文字エンコーディングの設定をオーバーライド可能に。
@@ -830,7 +959,7 @@
 	* Auto/Response.pm: plum でのキー名が response だったのを勘違いして reply にしていた。
 	  (りんりんさんバグレポートありがとうございます)
 
-2003-03-09  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-09  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Configuration/Block.pm: %CODE{ }EDOC%の解釈でメモリリークを起こす不具合を回避。
 
@@ -839,7 +968,7 @@
 	* Auto/AliasDB/CallbackUtils.pm: register_RandomNickConvertでメッセージがIrcIO::Client
 	  発信だった場合に登録しないようにした。 (りんりんさんバグレポートありがとうございます)
 
-2003-03-08  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-08  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IrcIO/Server.pm: オプションnetworks/always-notify-new-nickが設定されていたら
 	  nickを変更する度に、変更したサーバーの新しいグローバルnickをNOTICEで通知する。
@@ -872,7 +1001,7 @@
 	* LinedINETSocket.pm: ExternalSocketのuninstall等も必要かも知れないので
 	  DESTROYを復活させる。
 
-2003-03-04  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-04  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* ExternalSocket.pm: read/write/want_to_writeで、callerをチェックする位置を変更。
 
@@ -895,7 +1024,7 @@
 
 	* Auto/Response.pm: rateを使えるようにした。
 
-2003-03-03  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-03  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Configuration/Block.pm: ブロック内ブロックを扱えるように。
 	
@@ -939,12 +1068,12 @@
 
 	* Tools/GroupDB.pm: 新規追加。AliasDBから独立させた。
 
-2003-03-02  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-02  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Configuration/Preprocessor.pm: 新規追加。
 	  confファイルのプリプロセッサとして使用する。
 
-2003-03-01  phonohawk  <phonohawk@lycos.co.uk>
+2003-03-01  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* ConfigBlock.pm: 削除。Configuration::Blockに移動。
 
@@ -976,7 +1105,7 @@
 
 	* LinedINETSocket.pm: 新規追加。行単位のキューを使用した入出力を行う。
 
-2003-02-26  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-26  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* ExternalSocket.pm: 新規追加。
 
@@ -985,7 +1114,7 @@
 
 	* Timer.pm: uninstallした時はrunloopにundefを代入する。
 	
-2003-02-20  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-20  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Module.pm (notification_of_message_io) : 新規追加。
 	  サーバーやクライアントと実際に送受信したメッセージが通知されるメソッド。デバッグ用。
@@ -995,13 +1124,13 @@
 	* IrcIO/Client.pm : 複数のクライアントを接続している際、
 	  NICKの変更が他のクライアントへ伝わっていなかったので修正。
 
-2003-02-19  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-19  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.el: 新規追加。confファイルのEmacs用モード。試作品。
 	  このファイルが置かれている場所をload-pathに追加し、次のようなautoloadを実行すれば良い。
 	  (autoload 'tiarra-conf-mode "tiarra-conf" "tiarra.conf editing mode" t)
 
-2003-02-17  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-17  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Configuration.pm:
 		・@includeで他のファイルをインクルード可能に。
@@ -1029,7 +1158,7 @@
 
 	* Auto/Utils.pm: $get_ch_nameでparam(0)が無い場合undefを返すようにした。
 
-2003-02-15  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-15  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* RunLoop.pm: Time::HiResがインストールされている環境では
 	  Timerの精度がミリ秒に上がる。入っていなければ秒のまま。
@@ -1075,11 +1204,11 @@
 
 	* System/Pong.pm: xchat対策に、pingの代わりにpongを消滅させるようにした。
 
-2003-02-13  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-13  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Multicast.pm: nick_pでのnicklen制限をやめた。
 
-2003-02-12  phonohawk  <phonohawk@lycos.co.uk>
+2003-02-12  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* RunLoop.pm: $(network)/userでgeneral/userをオーバーライド可能に。
 
@@ -1087,7 +1216,7 @@
 
 	* これ以前のログは書いていません。
 
-#       Id: $Id: ChangeLog,v 1.124 2004/01/22 19:13:45 topia Exp $
+#       Id: $Id: ChangeLog,v 1.128 2004/02/14 11:48:17 topia Exp $
 #   Author: $Author: topia $
-#     Date: $Date: 2004/01/22 19:13:45 $
-# Revision: $Revision: 1.124 $
+#     Date: $Date: 2004/02/14 11:48:17 $
+# Revision: $Revision: 1.128 $
diff -urN tiarra-20040122/LICENSE tiarra-20040214/LICENSE
--- tiarra-20040122/LICENSE	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20040214/LICENSE	2004-02-15 01:45:21.000000000 +0900
@@ -0,0 +1,2 @@
+# This is free software; you can redistribute it and/or modify it
+#   under the same terms as Perl itself.
\ ファイル末尾に改行がありません
diff -urN tiarra-20040122/NEWS tiarra-20040214/NEWS
--- tiarra-20040122/NEWS	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/NEWS	2004-02-15 01:45:21.000000000 +0900
@@ -1,4 +1,36 @@
-2004-01-23  phonohawk  <phonohawk@lycos.co.uk>
+2004-02-14  Topia  <topia@clovery.jp>
+
+	* Log::Recent, System::Raw, Channel::Freeze, Channel::Rejoin
+	  - これらのモジュールのうち一つでも組み込んでいる場合は、
+	    リロードする前に Tiarra を再起動させてください。
+	    新たに入った機能を使っています。
+
+	* Client::Cache
+	  - 新規追加。クライアントからの問い合わせのうち、
+	    Tiarra が情報を持っていて、
+	    サーバに問い合わせる必要がないものをキャッシュとして返します。
+	  - いまのところ MODE キャッシュと、 WHO キャッシュを実装していて、
+	    どちらも、クライアントからの最初の問い合わせのときにのみ
+	    キャッシュを使います。
+	  - LimeChat(1.18 で WHO 機能を切れるようになりましたが) や、
+	    X-Chat などのクライアントを使用されている場合は、
+	    組み込むと便利です。
+
+	* Client::Eval
+	  - 追加。クライアントからのコマンドしか受け付けませんが、
+	    その代わりすべてのコマンドを実行できます。
+	    事実上 IRC パスワードがわかれば Tiarra が動いているホスト上で
+	    動作しているアカウントの権限で何でもできる、
+	    ということに注意してください。
+	  - 意味がわからなければ組み込まないことを推奨します。
+	    必要な時だけ組み込んで、すぐはずす、というのも良いかもしれません。
+
+2004-01-27  phonohawk  <phonohawk@ps.sakura.ne.jp>
+
+	* 行の終わりにCRLFでなくLFを付けるようなクライアントでも
+	正しく動作するようになりました。
+
+2004-01-23  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra: 起動時オプション --make-password 追加。
 	make-passwordの機能をtiarra本体に移した。
@@ -29,7 +61,7 @@
 	single-server-mode を使う予定が無い場合は再起動は不要です。
 	single-server-mode を使っている方は、アップデートして再起動して下さい。
 
-2003-10-16  phonohawk  <phonohawk@lycos.co.uk>
+2003-10-16  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* System::Raw
 	追加。Tiarraに改変されない生のメッセージをサーバーに送るためのモジュール。
@@ -39,7 +71,7 @@
 	* single-server-mode の bugfix です。
 	single-server-mode を使う予定が無い場合は再起動は不要です。
 
-2003-09-28  phonohawk  <phonohawk@lycos.co.uk>
+2003-09-28  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.el:
 	mmm-modeがインストールされていて、(require 'mmm-mode)または
@@ -55,7 +87,7 @@
 	モジュールのアンロードをすると原因不明のエラーが起こるバグがあります。
 	また、アップグレードの際には再起動が必要です。
 
-2003-08-12  phonohawk  <phonohawk@lycos.co.uk>
+2003-08-12  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* シングルサーバーモードを実装。
 	networks/multi-server-modeを0に設定すると、シングルサーバーモードになります。
@@ -74,27 +106,27 @@
 	  - Auto::Response
 	です。変更よろしくお願いします。(^^;;
 
-2003-07-10  phonohawk  <phonohawk@lycos.co.uk>
+2003-07-10  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Channel::Freeze
 	追加。特定のチャンネルのNOTICEやPRIVMSGの中継を
 	一時的に中断するためのモジュール。
 	発言を見たくないがPARTはしたくない、といった場合に有効。
 
-003-07-03  phonohawk  <phonohawk@lycos.co.uk>
+003-07-03  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Auto::Oper
 	複数の応答が定義されていれば、ランダムに一つ選んで発言する。省略も可能。
 
-2003-06-06  phonohawk  <phonohawk@lycos.co.uk>
+2003-06-06  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* general/nickを、それぞれのネットワーク設定ブロックのnickでオーバーライド可能に。
 
-2003-05-27  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-27  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Tiarra本体の誤動作によりCPU時間を食い潰している可能性を検出して警告する。
 
-2003-05-24  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-24  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* IPv6対応
 	general/tiarra-ip-versionに'v6'を指定する事で、IPv6でのリスニングを行なう。
@@ -109,23 +141,23 @@
 	* Auto/Alias.pm: キーを指定しての値削除、削除した個数の表示が可能になったため、
 	  サンプルの removed-format が変更されています。好みに合わせて変更してください。
 
-2003-05-21  phonohawk  <phonohawk@lycos.co.uk>
+2003-05-21  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* tiarra-conf.l: 追加。
 	  Noboruhiさんによるxyzzy用tiarra.conf編集モード。
 	  インストール方法はtiarra-conf.l内に記述されています。
 
-2003-04-29  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-29  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* Channel/Join/Kicked.pm: 追加。チャンネルから蹴られた時に、自動JOINするモジュール。
 
-2003-04-13  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-13  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* User/Vanish.pm: 追加
 	  特定のチャンネルでの特定の人物の存在をクライアントに隠すモジュール。
 	  JOINやPART、QUIT等を消去する。
 
-2003-04-05  phonohawk  <phonohawk@lycos.co.uk>
+2003-04-05  phonohawk  <phonohawk@ps.sakura.ne.jp>
 
 	* モジュール Auto::Joined 追加。
 	  特定のチャンネルに誰かがJOINする度に特定の発言を行なうモジュール。
@@ -136,7 +168,7 @@
 	* sample.conf (Auto/Random.pm): 設定に mask プロパティが抜けていました。
 	  Auto/Random.pm を使っていた方は、 sample.conf にしたがって適当なところに追加してください。
 
-2003-03-23  phonohawk <phonohawk@lycos.co.uk>
+2003-03-23  phonohawk <phonohawk@ps.sakura.ne.jp>
 
 	* User/Filter.pm: 新規追加。 特定の人物の発言にフィルタをかける。
 
diff -urN tiarra-20040122/main/ChannelInfo.pm tiarra-20040214/main/ChannelInfo.pm
--- tiarra-20040122/main/ChannelInfo.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/ChannelInfo.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: ChannelInfo.pm,v 1.13 2003/09/26 12:07:13 topia Exp $
+# $Id: ChannelInfo.pm,v 1.14 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # チャンネル情報を保持
 # -----------------------------------------------------------------------------
@@ -49,6 +49,51 @@
     scalar Multicast::attach($this->name,$this->network_name);
 }
 
+sub mode_string {
+    # RPL_CHANNELMODEIS の形式で返す。
+    my $this = shift;
+
+    my $str = '+';
+    my @param;
+    my ($checker, %hash);
+
+    # switches
+    $checker = sub {
+	my $key = shift;
+	if ($hash{$key}) {
+	    $str .= $key;
+	    delete $hash{$key}
+	}
+    };
+
+    %hash = %{$this->switches};
+    map {
+	$checker->($_);
+    } split //, 'spmtinaqr';
+    map {
+	$checker->($_);
+    } keys %hash;
+
+    # parameters
+    %hash = %{$this->parameters};
+    $checker = sub {
+	my $key = shift;
+	if ($hash{$key}) {
+	    $str .= $key;
+	    push(@param, $hash{$key});
+	    delete $hash{$key}
+	}
+    };
+    map {
+	$checker->($_);
+    } split //, 'lk';
+    map {
+	$checker->($_);
+    } keys %hash;
+
+    return ($str, @param);
+}
+
 my $types = {
     topic => 'scalar',
     topic_who => 'scalar',
diff -urN tiarra-20040122/main/Configuration.pm tiarra-20040214/main/Configuration.pm
--- tiarra-20040122/main/Configuration.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/Configuration.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Configuration.pm,v 1.21 2003/08/12 01:45:35 admin Exp $
+# $Id: Configuration.pm,v 1.22 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # このクラスはフック`reloaded'を用意します。
 # フック`reloaded'は、設定ファイルがリロードされた時に呼ばれます。
@@ -186,6 +186,7 @@
 	'client-in-encoding' => 'jis',
 	'client-out-encoding' => 'jis',
 	'stdout-encoding' => 'euc',
+	'sysmsg-prefix' => 'tiarra',
     },
     networks => {
 	'name' => 'main',
diff -urN tiarra-20040122/main/IRCMessage.pm tiarra-20040214/main/IRCMessage.pm
--- tiarra-20040122/main/IRCMessage.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/IRCMessage.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: IRCMessage.pm,v 1.16 2004/01/14 14:48:35 admin Exp $
+# $Id: IRCMessage.pm,v 1.17 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # IRCMessageはIRCのメッセージを表わすクラスです。実際のメッセージはUTF-8で保持します。
 # 生のメッセージのパース、シリアライズ、そしてメッセージの生成をサポートします。
@@ -29,12 +29,13 @@
 # $msg = new IRCMessage(Command => 'NOTICE',
 #                       Params => ['foo','hugahuga']);
 # print $msg->serialize('jis'); # "NOTICE foo :hugahuga"を表示
-#                       
+#
 package IRCMessage;
 use strict;
 use warnings;
 use Carp;
 use Unicode::Japanese;
+use Data::Dumper;
 
 use constant PREFIX  => 0;
 use constant COMMAND => 1;
@@ -52,13 +53,13 @@
     $obj->[PREFIX] = undef;
     $obj->[COMMAND] = undef;
     $obj->[PARAMS] = undef;
-    
+
     $obj->[NICK] = undef;
     $obj->[NAME] = undef;
     $obj->[HOST] = undef;
 
     $obj->[REMARKS] = undef;
-    
+
     if (exists $args{'Line'}) {
 	$args{'Line'} =~ s/\x0d\x0a$//s; # 行末のcrlfは消去。
 	$obj->_parse($args{'Line'},$args{'Encoding'} || 'auto'); # Encodingが省略されたら自動判別
@@ -87,7 +88,7 @@
 	else {
 	    die "You can't make IRCMessage without a COMMAND.\n";
 	}
-	
+
 	if (exists $args{'Params'}) {
 	    # Paramsがあった。型はスカラーもしくは配列リファ
 	    my $params = $args{'Params'};
@@ -114,9 +115,14 @@
 }
 
 sub clone {
-    my $this = shift;
-    my @new = @$this;
-    bless \@new => ref($this);
+    my ($this, %args) = @_;
+    if ($args{deep}) {
+	eval
+	    Data::Dumper->new([$this])->Terse(1)->Deepcopy(1)->Purity(1)->Dump;
+    } else {
+	my @new = @$this;
+	bless \@new => ref($this);
+    }
 }
 
 sub _parse {
@@ -124,7 +130,7 @@
     delete $this->[PREFIX];
     delete $this->[COMMAND];
     delete $this->[PARAMS];
-    
+
     my $pos = 0;
     # prefix
     if (substr($line,0,1) eq ':') {
@@ -145,7 +151,7 @@
 		$unicode->set($value_raw,$encoding)->utf8;
 	    }
 	};
-	
+
 	if ($this->[COMMAND]) {
 	    # commandはもう設定済み。次はパラメータだ。
 	    if ($this->[PARAMS]) {
@@ -194,7 +200,7 @@
     # 解釈結果の正当性をチェック。
     # commandが無かったらdie。
     unless ($this->[COMMAND]) {
-	die "IRCMessage parsed unvalid one, which doesn't have command.\n  $line\n";
+	die "IRCMessage parsed invalid one, which doesn't have command.\n  $line\n";
     }
 }
 
@@ -238,12 +244,12 @@
     if ($this->[PREFIX]) {
 	$result .= ':'.$this->[PREFIX].' ';
     }
-    
+
     $result .= $this->[COMMAND].' ';
-    
+
     if ($this->[PARAMS]) {
 	my $unicode = new Unicode::Japanese;
-	my $n_params = scalar @{$this->[PARAMS]};
+	my $n_params = scalar @{$this->[PARAMS]||[]};
 	for (my $i = 0;$i < $n_params;$i++) {
 	    if ($i == $n_params - 1) {
 		# 最後のパラメタなら頭にコロンを付けて後にはスペースを置かない。
@@ -315,7 +321,7 @@
 }
 
 sub n_params {
-    scalar @{$_[0]->[PARAMS]};
+    scalar @{$_[0]->[PARAMS]||[]};
 }
 
 sub param {
diff -urN tiarra-20040122/main/IrcIO/Client.pm tiarra-20040214/main/IrcIO/Client.pm
--- tiarra-20040122/main/IrcIO/Client.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/IrcIO/Client.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Client.pm,v 1.21 2003/09/26 12:07:13 topia Exp $
+# $Id: Client.pm,v 1.23 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # IrcIO::Clientはクライアントからの接続を受け、
 # IRCメッセージをやり取りするクラスです。
@@ -15,6 +15,7 @@
 use Multicast;
 use Mask;
 use LocalChannelManager;
+use NumericReply;
 
 use SelfLoader;
 SelfLoader->load_stubs; # このクラスには親クラスがあるから。(SelfLoaderのpodを参照)
@@ -111,7 +112,7 @@
     my ($this,$msg) = @_;
 
     # 各モジュールに通知
-    RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'out');
+    #RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'out');
 
     $this->SUPER::send_message(
 	$msg,
@@ -136,7 +137,7 @@
     # クライアントがログイン中なら、ログインを受け付ける。
     if (defined $msg) {
 	# 各モジュールに通知
-	RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'in');
+	#RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'in');
 
 	# ログイン作業中か？
 	if ($this->{logging_in}) {
@@ -181,14 +182,15 @@
     if ($this->{nick} ne '' && $this->{username} ne '') {
 	# general/tiarra-passwordを取得
 	my $valid_password = Configuration->shared_conf->general->tiarra_password;
+	my $prefix = Configuration->shared->general->sysmsg_prefix;
 	if (defined $valid_password && $valid_password ne '' &&
 	    ! Crypt::check($this->{pass_received},$valid_password)) {
 	    # パスワードが正しくない。
 	    ::printmsg("Refused login of ".$this->fullname_from_client." because of bad password.");
 
 	    $this->send_message(
-		new IRCMessage(Prefix => 'tiarra',
-			       Command => '464',
+		new IRCMessage(Prefix => $prefix,
+			       Command => ERR_PASSWDMISMATCH,
 			       Params => [$this->{nick},'Password incorrect']));
 	    $this->send_message(
 		new IRCMessage(Command => 'ERROR',
@@ -208,8 +210,8 @@
 	    $this->{logging_in} = 0;
 
 	    $this->send_message(
-		new IRCMessage(Prefix => 'tiarra',
-			       Command => '001',
+		new IRCMessage(Prefix => $prefix,
+			       Command => RPL_WELCOME,
 			       Params => [$this->{nick},'Welcome to the Internet Relay Network '.$this->fullname_from_client]));
 
 	    my $current_nick = RunLoop->shared_loop->current_nick;
@@ -221,25 +223,32 @@
 				   Param => $current_nick));
 	    }
 
+	    my $send_message = sub {
+		my ($command, @params) = @_;
+		$this->send_message(
+		    new IRCMessage(
+			Prefix => $prefix,
+			Command => $command,
+			Params => [$current_nick,
+				   @params],
+		       ));
+	    };
+
 	    map {
 		# ローカルnickとグローバルnickが食い違っていたらその旨を伝える。
 		my $network_name = $_->network_name;
 		my $global_nick = $_->current_nick;
 		if ($global_nick ne $current_nick) {
-		    $this->send_message(
-			new IRCMessage(Prefix => 'tiarra',
-				       Command => 'NOTICE',
-				       Params => [$current_nick,
-						  "*** Your global nick in $network_name is currently '$global_nick'."]));
+		    $send_message->('NOTICE', "*** Your global nick in $network_name is currently '$global_nick'.");
 		}
 	    } values %{RunLoop->shared_loop->networks};
 
+	    $send_message->(RPL_YOURHOST, "Your host is $prefix, running version ".::version());
+	    $send_message->(RPL_MOTDSTART, "- $prefix Message of the Day -");
 	    foreach my $line (main::get_credit()) {
-		$this->send_message(
-		    new IRCMessage(Prefix => 'tiarra',
-				   Command => '002',
-				   Params => [$current_nick,$line]));
+		$send_message->(RPL_MOTD, "- ".$line);
 	    }
+	    $send_message->(RPL_ENDOFMOTD, "End of MOTD command.");
 
 	    # joinしている全てのチャンネルの情報をクライアント送る。
 	    $this->inform_joinning_channels;
@@ -262,7 +271,7 @@
 	if (defined $msg->params) {
 	    # 形式が正しい限りNICKには常に成功して、RunLoopのカレントnickが変更になる。
 	    # ただしネットワーク名が明示されていた場合はカレントを変更しない。
-	    my ($nick,undef,$specified) = Multicast::detatch($msg->params->[0]);
+	    my ($nick,undef,$specified) = Multicast::detach($msg->params->[0]);
 	    if (Multicast::nick_p($nick)) {
 		unless ($specified) {
 		    #$this->send_message(
@@ -283,8 +292,8 @@
 	    } else {
 		$this->send_message(
 		    new IRCMessage(
-			Prefix => 'tiarra',
-			Command => '432',
+			Prefix => Configuration->shared->general->sysmsg_prefix,
+			Command => ERR_ERRONEOUSNICKNAME,
 			Params => [RunLoop->shared_loop->current_nick,
 				   $msg->params->[0],
 				   'Erroneous nickname']));
@@ -294,8 +303,8 @@
 	} else {
 	    $this->send_message(
 		new IRCMessage(
-		    Prefix => 'tiarra',
-		    Command => '431',
+		    Prefix => Configuration->shared->general->sysmsg_prefix,
+		    Command => ERR_NONICKNAMEGIVEN,
 		    Params => [RunLoop->shared_loop->current_nick,
 			       'No nickname given']));
 	    # これは鯖に送らない。
@@ -357,7 +366,7 @@
 		$this->send_message(
 		    IRCMessage->new(
 			Prefix => $this->fullname,
-			Command => '332',
+			Command => RPL_TOPIC,
 			Params => [$local_nick,$ch_name,$ch->topic]));
 	    }
 	    # 次にRPL_TOPICWHOTIME(あれば)
@@ -365,7 +374,7 @@
 		$this->send_message(
 		    IRCMessage->new(
 			Prefix => $this->fullname,
-			Command => '333',
+			Command => RPL_TOPICWHOTIME,
 			Params => [$local_nick,$ch_name,$ch->topic_who,$ch->topic_time]));
 	    }
 	    # 次にRPL_NAMREPLY
@@ -387,7 +396,7 @@
 		    $this->send_message(
 			IRCMessage->new(
 			    Prefix => $this->fullname,
-			    Command => '353',
+			    Command => RPL_NAMREPLY,
 			    Params => [$local_nick,
 				       $ch_property_char,
 				       $ch_name,
@@ -427,7 +436,7 @@
 	    $this->send_message(
 		IRCMessage->new(
 		    Prefix => $this->fullname,
-		    Command => '366',
+		    Command => RPL_ENDOFNAMES,
 		    Params => [$local_nick,$ch_name,'End of NAMES list']));
 	} values %{$network->channels};
     } values %{RunLoop->shared_loop->networks};
diff -urN tiarra-20040122/main/IrcIO/Server.pm tiarra-20040214/main/IrcIO/Server.pm
--- tiarra-20040122/main/IrcIO/Server.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/IrcIO/Server.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Server.pm,v 1.49 2003/10/24 15:52:29 admin Exp $
+# $Id: Server.pm,v 1.51 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # IrcIO::ServerはIRCサーバーに接続し、IRCメッセージをやり取りするクラスです。
 # このクラスはサーバーからメッセージを受け取ってチャンネル情報や現在のnickなどを保持しますが、
@@ -17,6 +17,7 @@
 use Configuration;
 use UNIVERSAL;
 use Multicast;
+use NumericReply;
 
 sub new {
     my ($class,$network_name) = @_;
@@ -303,7 +304,7 @@
     }
 
     # 各モジュールへ通知
-    RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'out');
+    #RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'out');
 
     $this->SUPER::send_message(
 	$msg,
@@ -313,7 +314,7 @@
 
 sub receive {
     my $this = shift;
-    my @receipts = $this->SUPER::receive(
+    $this->SUPER::receive(
 	Configuration->shared->get($this->{network_name})->in_encoding ||
 	Configuration->shared->general->server_in_encoding);
 
@@ -333,7 +334,7 @@
     # 接続を切ってからdieします。
     if (defined $msg) {
 	# 各モジュールに通知
-	RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'in');
+	#RunLoop->shared->notify_modules('notification_of_message_io',$msg,$this,'in');
 
 	# ログイン作業中か？
 	if ($this->{logged_in}) {
@@ -353,7 +354,7 @@
     # まだログイン作業中であるのなら、ログインに成功したかどうかを
     # 最初に受け取った行が001(成功)か433(nick重複)かそれ以外かで判断する。
     my $reply = $first_msg->command;
-    if ($reply eq '001') {
+    if ($reply eq RPL_WELCOME) {
 	# 成功した。
 	$this->{current_nick} = $first_msg->param(0);
 	if (!RunLoop->shared->multi_server_mode_p &&
@@ -382,12 +383,12 @@
 	}
 	$this->{new_connection} = undef;
     }
-    elsif ($reply eq '433') {
+    elsif ($reply eq ERR_NICKNAMEINUSE) {
 	# nick重複。
 	$this->_set_to_next_nick($first_msg->param(1));
 	return; # 何も返さない→クライアントにはこの結果を知らせない。
     }
-    elsif ($reply eq '437') {
+    elsif ($reply eq ERR_UNAVAILRESOURCE) {
 	# nick/channel is temporarily unavailable(この場合は nick)
 	$this->_set_to_next_nick($first_msg->param(1));
 	return; # 何も返さない→クライアントにはこの結果を知らせない。
@@ -442,7 +443,7 @@
 	}
 	$this->_NICK($msg);
     }
-    elsif ($msg->command eq '433') {
+    elsif ($msg->command eq ERR_NICKNAMEINUSE) {
 	# nickが既に使用中
 	if (RunLoop->shared->multi_server_mode_p) {
 	    $this->_set_to_next_nick($msg->param(1));
@@ -451,7 +452,7 @@
 	    $msg = undef;
 	}
     }
-    elsif ($msg->command eq '437') {
+    elsif ($msg->command eq ERR_UNAVAILRESOURCE) {
 	# nick/channel temporary unavaliable
 	if (Multicast::nick_p($msg->param(1)) && RunLoop->shared->multi_server_mode_p) {
 	    $this->_set_to_next_nick($msg->param(1));
@@ -482,50 +483,25 @@
     elsif ($msg->command eq 'TOPIC') {
 	$this->_TOPIC($msg);
     }
-    elsif ($msg->command eq '311') {
-	$this->_RPL_WHOISUSER($msg);
-    }
-    elsif ($msg->command eq '312') {
-	$this->_RPL_WHOISSERVER($msg);
-    }
-    elsif ($msg->command eq '324') {
-	$this->_RPL_CHANNELMODEIS($msg);
-    }
-    elsif ($msg->command eq '331') {
-	$this->_RPL_NOTOPIC($msg);
-    }
-    elsif ($msg->command eq '332') {
-	$this->_RPL_TOPIC($msg);
-    }
-    elsif ($msg->command eq '333') {
-	$this->_RPL_TOPICWHOTIME($msg);
-    }
-    elsif ($msg->command eq '346') {
-	$this->_RPL_INVITELIST($msg);
-    }
-    elsif ($msg->command eq '347') {
-	$this->_RPL_ENDOFINVITELIST($msg);
-    }
-    elsif ($msg->command eq '348') {
-	$this->_RPL_EXCEPTLIST($msg);
-    }
-    elsif ($msg->command eq '349') {
-	$this->_RPL_ENDOFEXCEPTLIST($msg);
-    }
-    elsif ($msg->command eq '352') {
-	$this->_RPL_WHOREPLY($msg);
-    }
-    elsif ($msg->command eq '353') {
-	$this->_RPL_NAMREPLY($msg);
-    }
-    elsif ($msg->command eq '366') {
-	$this->_RPL_ENDOFNAMES($msg);
-    }
-    elsif ($msg->command eq '367') {
-	$this->_RPL_BANLIST($msg);
-    }
-    elsif ($msg->command eq '368') {
-	$this->_RPL_ENDOFBANLIST($msg);
+    else {
+	my $name = NumericReply::fetch_name($msg->command);
+	if (defined $name) {
+	    foreach (
+		map("RPL_$_",
+		    qw(CHANNELMODEIS NOTOPIC TOPIC TOPICWHOTIME
+		       WHOREPLY NAMREPLY ENDOFNAMES
+		       WHOISUSER WHOISSERVER AWAY ENDOFWHOIS),
+		    map({("${_}LIST", "ENDOF${_}LIST");}
+			    qw(INVITE EXCEPT BAN)),
+		   )) {
+		if ($name eq $_) {
+		    no strict 'refs';
+		    my $funcname = "_$_";
+		    &$funcname($this, $msg); # $this->$funcname($msg)
+		    last;
+		}
+	    }
+	}
     }
     return $msg;
 }
@@ -821,6 +797,32 @@
 	$p->userhost($msg->param(3));
 	$p->realname($msg->param(5));
     }
+    $this->_START_WHOIS_REPLY($p);
+}
+
+sub _START_WHOIS_REPLY {
+    my ($this,$p) = @_;
+    $p->remark('wait-rpl_away', 1);
+}
+
+sub _RPL_ENDOFWHOIS {
+    my ($this,$msg) = @_;
+    my $p = $this->{people}->{$msg->param(1)};
+    if (defined $p) {
+	if ($p->remark('wait-rpl_away')) {
+	    $p->remark('wait-rpl_away', 0);
+	    $p->away('');
+	}
+    }
+}
+
+sub _RPL_AWAY {
+    my ($this,$msg) = @_;
+    my $p = $this->{people}->{$msg->param(1)};
+    if (defined $p) {
+	$p->remark('wait-rpl_away', 0);
+	$p->away($msg->param(2));
+    }
 }
 
 sub _RPL_WHOISSERVER {
@@ -938,7 +940,15 @@
 	$p->username($msg->param(2));
 	$p->userhost($msg->param(3));
 	$p->server($msg->param(4));
-	$p->realname((split / /,$msg->param(7))[1]);
+	$p->realname((split / /,$msg->param(7),2)[1]);
+	if ($msg->param(6) =~ /^G/) {
+	    $p->away('Gone.');
+	} else {
+	    $p->away('');
+	}
+	my $hops = $this->remark('server-hops') || {};
+	$hops->{$p->server} = (split / /,$msg->param(7),2)[0];
+	$this->remark('server-hops', $hops);
     }
 
     #use Data::Dumper;
@@ -959,6 +969,10 @@
 	$ch->remarks('switches-are-known',1);
     }
 
+    # switches と parameters は必ず得られると仮定して、クリア処理を行う
+    $ch->switches(undef, undef, 'clear');
+    $ch->parameters(undef, undef, 'clear');
+
     # 鯖がMODEを実行したことにして、_MODEに処理を代行させる。
     my @args = @{$msg->params};
     @args = @args[1 .. $#args];
diff -urN tiarra-20040122/main/IrcIO.pm tiarra-20040214/main/IrcIO.pm
--- tiarra-20040122/main/IrcIO.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/IrcIO.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: IrcIO.pm,v 1.19 2003/09/20 11:06:19 admin Exp $
+# $Id: IrcIO.pm,v 1.21 2004/02/04 12:08:54 admin Exp $
 # -----------------------------------------------------------------------------
 # IrcIOはIRCサーバー又はクライアントと接続し、IRCメッセージをやり取りする抽象クラスです。
 # -----------------------------------------------------------------------------
@@ -82,10 +82,18 @@
     # また、生の文字列については文字コードの変換が行なわれない。
     my $data_to_send = '';
     if (ref($msg) eq '') {
+	# deprecated.
+	# FIXME: warnすべきだろうか。
 	$data_to_send = "$msg\x0d\x0a";
     }
     elsif ($msg->isa('IRCMessage')) {
-	$data_to_send = $msg->serialize($encoding)."\x0d\x0a";
+	# message_io_hook
+	my $filtered = RunLoop->shared->apply_filters(
+	    [$msg], 'message_io_hook', $this, 'out');
+	foreach (@$filtered) {
+	    $data_to_send .= $_->serialize($encoding)."\x0d\x0a";
+	}
+	#$data_to_send = $msg->serialize($encoding)."\x0d\x0a";
     }
     else {
 	die "IrcIO::send_message : parameter msg was invalid; $msg\n";
@@ -143,18 +151,30 @@
     }
     
     while (1) {
-	# CRLFが行の終わり。	
-	my $crlf_pos = index($this->{recvbuf},"\x0d\x0a");
-	if ($crlf_pos == -1) {
+	# CRLFまたはLFが行の終わり。	
+	my $newline_pos = index($this->{recvbuf},"\x0a");
+	if ($newline_pos == -1) {
 	    # 一行分のデータが届いていない。
 	    last;
 	}
 
-	my $current_line = substr($this->{recvbuf},0,$crlf_pos);
-	$this->{recvbuf} = substr($this->{recvbuf},$crlf_pos+2);
+	my $current_line = substr($this->{recvbuf},0,$newline_pos);
+	$this->{recvbuf} = substr($this->{recvbuf},$newline_pos+1);
+
+	# CRLFだった場合、末尾にCRが付いているので取る。
+	$current_line =~ s/\x0d$//;
 
-	push @{$this->{recv_queue}},IRCMessage->new(Line => $current_line,
-						    Encoding => $encoding);
+	# message_io_hook
+	my $msg = IRCMessage->new(
+	    Line => $current_line, Encoding => $encoding);
+	my $filtered = RunLoop->shared->apply_filters(
+	    [$msg], 'message_io_hook', $this, 'in');
+	
+	foreach (@$filtered) {
+	    push @{$this->{recv_queue}}, $_;
+	}
+	#push @{$this->{recv_queue}},IRCMessage->new(
+	#    Line => $current_line, Encoding => $encoding);
     }
 }
 
diff -urN tiarra-20040122/main/Module.pm tiarra-20040214/main/Module.pm
--- tiarra-20040122/main/Module.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/Module.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Module.pm,v 1.10 2003/05/15 12:11:11 admin Exp $
+# $Id: Module.pm,v 1.11 2004/02/04 12:08:54 admin Exp $
 # -----------------------------------------------------------------------------
 # Tiarraモジュール(プラグイン)を表わす抽象クラスです。
 # 全てのTiarraモジュールはこのクラスを継承し、
@@ -92,12 +92,14 @@
     #         切断したサーバー。
 }
 
-sub notification_of_message_io {
+sub message_io_hook {
     my ($this,$message,$io,$type) = @_;
     # サーバーから受け取ったメッセージ、サーバーに送ったメッセージ、
     # クライアントから受け取ったメッセージ、クライアントに送ったメッセージは
-    # このメソッドで各モジュールに通知される。通知されるだけで変更する事は出来ない。
-    # 戻り値は無し。
+    # このメソッドで各モジュールに通知される。メッセージの変更も可能で、
+    # 戻り値のルールはmessage_arrivedと同じ。
+    #
+    # 通常のモジュールはこのメソッドを実装する必要は無い。
     #
     # $message :
     #    内容: IRCMessageオブジェクト
@@ -108,6 +110,7 @@
     # $type :
     #    内容: 文字列
     #         'in'なら受信、'out'なら送信
+    return $message;
 }
 
 sub control_requested {
diff -urN tiarra-20040122/main/ModuleManager.pm tiarra-20040214/main/ModuleManager.pm
--- tiarra-20040122/main/ModuleManager.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/main/ModuleManager.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: ModuleManager.pm,v 1.12 2003/09/25 13:15:59 topia Exp $
+# $Id: ModuleManager.pm,v 1.13 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # このクラスは全てのTiarraモジュールを管理します。
 # モジュールをロードし、リロードし、破棄するのはこのクラスです。
@@ -258,6 +258,9 @@
 		}
 
 		my $conf_block = $this->{mod_configs}->{$modname};
+		# message_io_hook が定義されているモジュールが死ぬと怖いので
+		# とりあえず undef を入れて無視させる
+		$this->{modules}->[$idx] = undef;
 		$this->_unload($conf_block);
 		$this->{modules}->[$idx] = $this->_load($conf_block); # 失敗するとundefが入る。
 	    }
diff -urN tiarra-20040122/main/Multicast.pm tiarra-20040214/main/Multicast.pm
--- tiarra-20040122/main/Multicast.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/main/Multicast.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Multicast.pm,v 1.19 2003/10/14 04:47:59 topia Exp $
+# $Id: Multicast.pm,v 1.20 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # サーバーからクライアントにメッセージが流れるとき、このクラスはフィルタとして
 # ネットワーク名を付加します。
@@ -107,7 +107,7 @@
 
 	    $message->params->[0] = $raw_channel;
 	    $message->params->[1] = local_to_global($raw_nick,$runloop->networks->{$to});
-	    forward_to_server($message,$to);		    
+	    forward_to_server($message,$to);
 	}
     }
     elsif (@channels == 1) {
@@ -208,7 +208,8 @@
 	$message->param(0) eq $global_nick &&
 	$local_nick ne $global_nick) {
 	$sender->send_message(
-	    new IRCMessage(Command => 'NOTICE',
+	    new IRCMessage(Prefix => Configuration->shared->general->sysmsg_prefix,
+			   Command => 'NOTICE',
 			   Params => [$local_nick,
 				      "*** Your global nick in $to is currently '$global_nick'."]));
     }
@@ -420,7 +421,11 @@
 	$default_network = $networks->default;
     }
     else {
-	$default_network = (RunLoop->shared->networks_list)[0]->network_name;
+	if (scalar RunLoop->shared->networks_list) {
+	    $default_network = (RunLoop->shared->networks_list)[0]->network_name;
+	} else {
+	    $default_network = $networks->default;
+	}
     }
 
     $separator = $networks->channel_network_separator;
diff -urN tiarra-20040122/main/NumericReply.pm tiarra-20040214/main/NumericReply.pm
--- tiarra-20040122/main/NumericReply.pm	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20040214/main/NumericReply.pm	2004-02-15 01:45:21.000000000 +0900
@@ -0,0 +1,263 @@
+# -----------------------------------------------------------------------------
+# $Id: NumericReply.pm,v 1.2 2004/02/14 11:48:19 topia Exp $
+# -----------------------------------------------------------------------------
+# このファイルでは、各ニューメリックリプライに対するシンボルを定義し、exportします。
+# -----------------------------------------------------------------------------
+package NumericReply;
+use strict;
+use warnings;
+use base qw/Exporter/;
+our @EXPORT;
+our %replies;
+our %numbers;
+
+BEGIN {
+    my @replies =
+	split(/\n/,
+	      q{
+	 --------------------- 通常メッセージ
+	 RPL_WELCOME          001
+	 RPL_YOURHOST         002
+	 RPL_CREATED          003
+	 RPL_MYINFO           004
+	 RPL_ISUPPORT         005
+	 RPL_BOUNCE           010
+	 RPL_MAP              015
+	 RPL_MAPMORE          016
+	 RPL_MAPEND           017
+	 RPL_MAPSTART         018
+
+
+	 RPL_NONE             300
+	 RPL_AWAY             301
+	 RPL_USERHOST         302
+	 RPL_ISON             303
+	 RPL_TEXT             304
+	 RPL_UNAWAY           305
+	 RPL_NOWAWAY          306
+
+	 RPL_WHOISUSER        311
+	 RPL_WHOISSERVER      312
+	 RPL_WHOISOPERATOR    313
+
+	 RPL_WHOWASUSER       314
+	 # RPL_ENDOFWHO 315: below
+	 RPL_ENDOFWHOWAS      369
+
+	 RPL_WHOISCHANOP      316 # redundant and not needed but reserved
+	 RPL_WHOISIDLE        317
+
+	 RPL_ENDOFWHOIS       318
+	 RPL_WHOISCHANNELS    319
+
+	 RPL_LISTSTART        321
+	 RPL_LIST             322
+	 RPL_LISTEND          323
+	 RPL_CHANNELMODEIS    324
+	 RPL_UNIQOPIS         325
+
+	 RPL_NOTOPIC          331
+	 RPL_TOPIC            332
+	 RPL_TOPICWHOTIME     333
+
+	 RPL_INVITING         341
+	 RPL_SUMMONING        342
+
+	 RPL_INVITELIST       346
+	 RPL_ENDOFINVITELIST  347
+
+	 RPL_EXCEPTLIST       348
+	 RPL_ENDOFEXCEPTLIST  349
+
+	 RPL_VERSION          351
+
+	 RPL_WHOREPLY         352
+	 RPL_ENDOFWHO         315
+	 RPL_NAMREPLY         353
+	 RPL_ENDOFNAMES       366
+
+	 RPL_KILLDONE         361
+	 RPL_CLOSING          362
+	 RPL_CLOSEEND         362
+	 RPL_LINKS            364
+	 RPL_ENDOFLINKS       365
+	 # RPL_ENDOFNAMES 366: above
+	 RPL_BANLIST          367
+	 RPL_ENDOFBANLIST     368
+	 # RPL_ENDOFWHOWAS 369: above
+
+	 RPL_INFO             371
+	 RPL_MOTD             372
+	 RPL_INFOSTART        373
+	 RPL_ENDOFINFO        374
+	 RPL_MOTDSTART        375
+	 RPL_ENDOFMOTD        376
+
+	 RPL_YOUREOPER        381
+	 RPL_REHASHING        382
+	 RPL_YOURESERVICE     383
+	 RPL_MYPORTIS         384
+	 RPL_NOTOPERANYMORE   385
+
+	 RPL_TIME             391
+	 RPL_USERSTART        392
+	 RPL_USERS            393
+	 RPL_ENDOFUSERS       394
+	 RPL_NOUSERS          395
+
+
+	 RPL_TRACELINK        200
+	 RPL_TRACECONNECTING  201
+	 RPL_TRACEHANDSHAKE   202
+	 RPL_TRACEUNKNOWN     203
+	 RPL_TRACEOPERATOR    204
+	 RPL_TRACEUSER        205
+	 RPL_TRACESERVER      206
+	 RPL_TRACESERVICE     207
+	 RPL_TRACENEWTYPE     208
+	 RPL_TRACECLASS       209
+	 RPL_TRACERECONNECT   210
+
+	 RPL_STATSLINKINFO    211
+	 RPL_STATSCOMMANDS    212
+	 RPL_STATSCLINE       213
+	 RPL_STATSNLINE       214
+	 RPL_STATSILINE       215
+	 RPL_STATSKLINE       216
+	 RPL_STATSQLINE       217
+	 RPL_STATSYLINE       218
+	 RPL_ENDOFSTATS       219
+	 RPL_STATSPLINE       220
+	 RPL_UMODEIS          221
+
+	 RPL_SERVICEINFO      231
+	 RPL_ENDOFSERVICE     232
+	 RPL_SERVICE          233
+	 RPL_SERVLIST         234
+	 RPL_SERVLISTEND      235
+
+	 RPL_STATSIAUTH       239
+	 RPL_STATSVLINE       240
+	 RPL_STATSLLINE       241
+	 RPL_STATSUPTIME      242
+	 RPL_STATSOLINE       243
+	 RPL_STATSHLINE       244
+	 RPL_STATSSLINE       245
+	 RPL_STATSPING        246
+	 RPL_STATSBLINE       247
+	 RPL_STATSDEFINE      248
+	 RPL_STATSDEBUG       249
+	 RPL_STATSDLINE       250
+
+	 RPL_LUSERCLIENT      251
+	 RPL_LUSEROP          252
+	 RPL_LUSERUNKNOWN     253
+	 RPL_LUSERCHANNELS    254
+	 RPL_LUSERME          255
+	 RPL_ADMINME          256
+	 RPL_ADMINLOC1        257
+	 RPL_ADMINLOC2        258
+	 RPL_ADMINEMAIL       259
+
+	 RPL_TRACELOG         261
+	 RPL_TRACEEND         262
+	 RPL_TRYAGAIN         263
+
+	 RPL_LOCALUSERS       265
+	 RPL_GLOBALUSERS      266
+
+
+	 --------------------- エラーメッセージ
+	 ERR_NOSUCHNICK       401
+	 ERR_NOSUCHSERVER     402
+	 ERR_NOSUCHCHANNEL    403
+	 ERR_CANNOTSENDTOCHAN 404
+	 ERR_TOOMANYCHANNELS  405
+	 ERR_WASNOSUCHNICK    406
+	 ERR_TOOMANYTARGETS   407
+	 ERR_NOSUCHSERVICE    408
+	 ERR_NOORIGIN         409
+
+	 ERR_NORECIPIENT      411
+	 ERR_NOTEXTTOSEND     412
+	 ERR_NOTOPLEVEL       413
+	 ERR_WILDTOPLEVEL     414
+	 ERR_BADMASK          415
+	 ERR_TOOMANYMATCHES   416
+
+	 ERR_UNKNOWNCOMMAND   421
+	 ERR_NOMOTD           422
+	 ERR_NOADMININFO      423
+	 ERR_FILEERROR        424
+
+	 ERR_NONICKNAMEGIVEN  431
+	 ERR_ERRONEOUSNICKNAME 432
+	 ERR_NICKNAMEINUSE    433
+	 ERR_SERVICENAMEINUSE 434
+	 ERR_SERVICECONFUSED  435
+	 ERR_NICKCOLLISION    436
+	 ERR_UNAVAILRESOURCE  437
+	 # ERR_DEAD 438: reserved for later use -krys
+
+	 ERR_USERNOTINCHANNEL 441
+	 ERR_NOTONCHANNEL     442
+	 ERR_USERONCHANNEL    443
+	 ERR_NOLOGIN          444
+	 ERR_SUMMONDISABLED   445
+	 ERR_USERSDISABLED    446
+
+	 ERR_NOTREGISTERED    451
+
+	 ERR_NEEDMOREPARAMS   461
+	 ERR_ALREADYREGISTRED 462
+	 ERR_NOPERMFORHOST    463
+	 ERR_PASSWDMISMATCH   464
+	 ERR_YOUREBANNEDCREEP 465
+	 ERR_YOUWILLBEBANNED  466
+	 ERR_KEYSET           467
+
+	 ERR_CHANNELISFULL    471
+	 ERR_UNKNOWNMODE      472
+	 ERR_INVITEONLYCHAN   473
+	 ERR_BANNEDFROMCHAN   474
+	 ERR_BADCHANNELKEY    475
+	 ERR_BADCHANMASK      476
+	 ERR_NOCHANMODES      477
+	 ERR_BANLISTFULL      478
+
+	 ERR_NOPRIVILEGES     481
+	 ERR_CHANOPRIVSNEEDED 482
+	 ERR_CANTKILLSERVER   483
+	 ERR_RESTRICTED       484
+	 ERR_UNIQOPRIVSNEEDED 485
+
+	 ERR_NOOPERHOST       491
+	 ERR_NOSERVICEHOST    492
+
+
+	 ERR_UMODEUNKNOWNFLAG 501
+	 ERR_USERSDONTMATCH   502
+     });
+    foreach (@replies) {
+	s/^\s+//;
+	my ($key,$value,$comment) = split(/\s+/, $_, 3);
+	next unless defined $key; # キー名
+	next if $key =~ /^-/; # コメント
+	next if $key =~ /^#/; # コメント
+	no strict 'refs';
+	*$key = sub (){ $value; };
+	$replies{$key} = $value;
+	$numbers{$value} = $key;
+	push @EXPORT, $key;
+    }
+}
+
+sub fetch_number {
+    $replies{+shift};
+}
+
+sub fetch_name {
+    $numbers{+shift};
+}
+
+1;
diff -urN tiarra-20040122/main/PersonInChannel.pm tiarra-20040214/main/PersonInChannel.pm
--- tiarra-20040122/main/PersonInChannel.pm	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/main/PersonInChannel.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,6 +1,6 @@
 # -*- cperl -*-
 # -----------------------------------------------------------------------------
-# $Id: PersonInChannel.pm,v 1.7 2003/09/22 19:23:06 admin Exp $
+# $Id: PersonInChannel.pm,v 1.8 2004/02/14 11:48:19 topia Exp $
 # -----------------------------------------------------------------------------
 # なるとや発言権を持っているかどうかの情報とPersonalInfoのセット。
 # -----------------------------------------------------------------------------
@@ -47,6 +47,14 @@
     $this->[HAS_V];
 }
 
+sub priv_symbol {
+    my $this = shift;
+
+    return '@' if ($this->has_o);
+    return '+' if ($this->has_v);
+    return '';
+}
+
 *remarks = \&remark;
 sub remark {
     my ($this,$key,$value) = @_;
diff -urN tiarra-20040122/main/PersonalInfo.pm tiarra-20040214/main/PersonalInfo.pm
--- tiarra-20040122/main/PersonalInfo.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/main/PersonalInfo.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: PersonalInfo.pm,v 1.8 2003/09/28 05:15:22 admin Exp $
+# $Id: PersonalInfo.pm,v 1.9 2004/02/14 11:48:19 topia Exp $
 # -----------------------------------------------------------------------------
 # nick,username,userhost等を持つ個人情報保持クラス。
 # このオブジェクトはIrcIO::Serverが管理する。
@@ -20,6 +20,7 @@
 use constant REALNAME => 3;
 use constant SERVER   => 4;
 use constant REMARK   => 5;
+use constant AWAY     => 6;
 
 sub new {
     my ($class,%args) = @_;
@@ -37,6 +38,7 @@
     $obj->[REALNAME] = $def_or_null->($args{RealName});
     $obj->[SERVER] = $def_or_null->($args{Server});
     $obj->[REMARK] = undef; # HASH
+    $obj->[AWAY] = $def_or_null->($args{Away});
 
     $obj;
 }
@@ -49,7 +51,7 @@
 }
 
 BEGIN {
-    foreach my $constname (qw/NICK USERNAME USERHOST REALNAME SERVER/) {
+    foreach my $constname (qw/NICK USERNAME USERHOST REALNAME SERVER AWAY/) {
 	my $methodname = lc $constname;
 	eval qq{
 	    sub $methodname {
diff -urN tiarra-20040122/main/RunLoop.pm tiarra-20040214/main/RunLoop.pm
--- tiarra-20040122/main/RunLoop.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/main/RunLoop.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: RunLoop.pm,v 1.49 2003/11/09 08:24:54 topia Exp $
+# $Id: RunLoop.pm,v 1.51 2004/02/14 11:48:19 topia Exp $
 # -----------------------------------------------------------------------------
 # このクラスはTiarraのメインループを実装します。
 # select()を実行し、サーバーやクライアントとのI/Oを行うのはこのクラスです。
@@ -363,6 +363,7 @@
 	$this->_rejoin_all_channels($network);
 	$this->broadcast_to_clients(
 	    IRCMessage->new(
+		Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 		Command => 'NOTICE',
 		Params => [$this->current_nick,
 			   '*** The connection has been revived between '.$network->network_name.'.']));
@@ -370,6 +371,7 @@
     elsif ($event eq 'disconnected') {
 	$this->broadcast_to_clients(
 	    IRCMessage->new(
+		Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 		Command => 'NOTICE',
 		Params => [$this->current_nick,
 			   '*** The connection has been broken between '.$network->network_name.'.']));
@@ -382,23 +384,31 @@
 	$this->_rejoin_all_channels($network);
 
 	my $msg = IRCMessage->new(
-	    Prefix => 'Tiarra',
+	    Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 	    Command => 'NOTICE',
 	    Params => ['', # チャンネル名は後で設定。
 		       '*** The connection has been revived between '.$network->network_name.'.']);
 	foreach my $ch (values %{$network->channels}) {
-	    $msg->param(0,Multicast::attach($ch->name,$network_name));
+	    if ($this->{multi_server_mode}) {
+		$msg->param(0,Multicast::attach($ch->name,$network_name));
+	    } else {
+		$msg->param(0,$ch->name);
+	    }
 	    $this->broadcast_to_clients($msg);
 	}
     }
     elsif ($event eq 'disconnected') {
 	my $msg = IRCMessage->new(
-	    Prefix => 'Tiarra',
+	    Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 	    Command => 'NOTICE',
 	    Params => ['', # チャンネル名は後で設定。
 		       '*** The connection has been broken between '.$network->network_name.'.']);
 	foreach my $ch (values %{$network->channels}) {
-	    $msg->param(0,Multicast::attach($ch->name,$network_name));
+	    if ($this->{multi_server_mode}) {
+		$msg->param(0,Multicast::attach($ch->name,$network_name));
+	    } else {
+		$msg->param(0,$ch->name);
+	    }
 	    $this->broadcast_to_clients($msg);
 	}
     }
@@ -519,7 +529,11 @@
 		$this->{receive_selector}->add($network->sock); # 受信セレクタに登録
 	    }
 	}; if ($@) {
-	    print $@;
+	    if ($@ =~ /^[Cc]ouldn't connect to /i) {
+		::printmsg($@);
+	    } else {
+		$this->notify_error($@);
+	    }
 	    # タイマー作り直し。
 	    $do_update_networks_after = 3;
 	}
@@ -923,7 +937,7 @@
 			push @{$this->{clients}},$client;
 			$this->{receive_selector}->add($new_sock);
 		    }; if ($@) {
-			print "$@\n";
+			$this->notify_msg($@);
 		    }
 		}
 	    }
@@ -1100,9 +1114,65 @@
     }
 }
 
+sub apply_filters {
+    # @extra_args: モジュールに送られる第二引数以降。第一引数は常にIRCMessage。
+    my ($this, $src_messages, $method, @extra_args) = @_;
+    my $mods = ModuleManager->shared_manager->get_modules;
+
+    my $source = $src_messages;
+    my $filtered = [];
+    foreach my $mod (@$mods) {
+	# (普通ないはずだが) $mod が undef だったらこのモジュールをとばす。
+	next unless defined $mod;
+	# sourceが空だったらここで終わり。
+	if (scalar(@$source) == 0) {
+	    return $source;
+	}
+	
+	foreach my $src (@$source) {
+	    my @reply = ();
+	    # 実行
+	    eval {
+		@reply = $mod->$method($src, @extra_args);
+	    }; if ($@) {
+		$this->notify_error(
+		    "Exception in ".ref($mod).".\n".
+		      "The message was '".$src->serialize."'.\n".
+			"   $@");
+	    }
+	    
+	    if (defined $reply[0]) {
+		# 値が一つ以上返ってきた。
+		# 全てIRCMessageのオブジェクトなら良いが、そうでなければエラー。
+		foreach my $msg_reply (@reply) {
+		    unless (UNIVERSAL::isa($msg_reply,'IRCMessage')) {
+			$this->notify_error(
+			    "Reply of ".ref($mod)."::${method} contains illegal value.\n".
+			      "It is ".ref($msg_reply).".");
+			return $source;
+		    }
+		}
+		
+		# これをfilteredに追加。
+		push @$filtered,@reply;
+	    }	    
+	}
+
+	# 次のsourceはfilteredに。filteredは空の配列に。
+	$source = $filtered;
+	$filtered = [];
+    }
+    return $source;
+}
+
 sub _apply_filters {
     # src_messagesは変更しない。
-    my ($this,$src_messages,$sender) = @_;
+    my ($this, $src_messages, $sender) = @_;
+    $this->apply_filters(
+	$src_messages, 'message_arrived', $sender);
+
+=pod
+    
     my $mods = ModuleManager->shared_manager->get_modules;
 
     my $source = $src_messages;
@@ -1146,6 +1216,9 @@
 	$filtered = [];
     }
     return $source;
+
+=cut
+
 }
 
 sub notify_error {
@@ -1174,6 +1247,7 @@
 	    $this->broadcast_to_clients(
 		map {
 		    IRCMessage->new(
+			Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 			Command => 'NOTICE',
 			Params => [$this->current_nick,
 				   "*** $_"]);
diff -urN tiarra-20040122/module/Channel/Freeze.pm tiarra-20040214/module/Channel/Freeze.pm
--- tiarra-20040122/module/Channel/Freeze.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Channel/Freeze.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Freeze.pm,v 1.3 2003/10/19 12:32:19 admin Exp $
+# $Id: Freeze.pm,v 1.4 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # このモジュールは再起動しても凍結設定を失はないやうにする爲、
 # 設定をBulletinBoardのfrost-channelsに保存します。
@@ -12,6 +12,7 @@
 use Timer;
 use BulletinBoard;
 use Mask;
+use Configuration;
 
 sub new {
     my $class = shift;
@@ -70,6 +71,7 @@
 	# 報告
 	RunLoop->shared->broadcast_to_clients(
 	    IRCMessage->new(
+		Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 		Command => 'NOTICE',
 		Params => [
 		    RunLoop->shared->current_nick,
@@ -88,6 +90,7 @@
 	    my $notice = shift;
 	    RunLoop->shared->broadcast_to_clients(
 		IRCMessage->new(
+		    Prefix => Configuration->shared_conf->general->sysmsg_prefix,
 		    Command => 'NOTICE',
 		    Params => [
 			RunLoop->shared->current_nick,
diff -urN tiarra-20040122/module/Channel/Join/Connect.pm tiarra-20040214/module/Channel/Join/Connect.pm
--- tiarra-20040122/module/Channel/Join/Connect.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Channel/Join/Connect.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Connect.pm,v 1.3 2003/09/25 13:15:59 topia Exp $
+# $Id: Connect.pm,v 1.4 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # copyright (C) 2002 Topia <topia@clovery.jp>. all rights reserved.
 package Channel::Join::Connect;
@@ -22,7 +22,7 @@
 sub _init {
     my $this = shift;
     foreach ($this->config->channel('all')) {
-	s/(,)\s+/$1/; # コンマの直後にスペースがあった場合、削除する
+	s/(,)\s+/$1/g; # コンマの直後にスペースがあった場合、削除する
 	my ($fullname, $key) = split(/\s+/, $_, 2);
 	my @fullnames = split(/\,/, $fullname);
 	my @keys = split(/,/, $key || '');
diff -urN tiarra-20040122/module/Channel/Rejoin.pm tiarra-20040214/module/Channel/Rejoin.pm
--- tiarra-20040122/module/Channel/Rejoin.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Channel/Rejoin.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Rejoin.pm,v 1.2 2003/01/24 04:00:52 admin Exp $
+# $Id: Rejoin.pm,v 1.3 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # このモジュールは動作時に掲示板のdo-not-touch-mode-of-channelsを使います。
 # -----------------------------------------------------------------------------
@@ -10,6 +10,7 @@
 use BulletinBoard;
 use Multicast;
 use RunLoop;
+use NumericReply;
 
 sub new {
     my $class = shift;
@@ -231,39 +232,33 @@
 	}
     };
     
-    if ($msg->command eq '324') {
+    if ($msg->command eq RPL_CHANNELMODEIS) {
 	# MODEリプライ
 	$session = $this->{sessions}->{$msg->param(1)};
 	if (defined $session) {
 	    $session->{got_mode} = 1;
 	    my $ch = $session->{ch};
 	    
-	    my $switches = join('',keys %{$ch->switches});
-	    my $params_key = '';
-	    my @params_val;
-	    while (my ($key,$value) = each %{$ch->parameters}) {
-		$params_key .= $key;
-		push @params_val,$value;
-	    }
-	    if (length($switches . $params_key) > 0) {
+	    my ($params, @params) = $ch->mode_string;
+	    if (length($params) > 1) {
 		# 設定すべきモードがある。
 		push @{$session->{cmd_buf}},IRCMessage->new(
 		    Command => 'MODE',
 		    Params => [$session->{ch_shortname},
-			       "+${switches}${params_key}",
-			       @params_val]);
+			       $params,
+			       @params]);
 	    }
 	}
     }
-    elsif ($msg->command eq '368') {
+    elsif ($msg->command eq RPL_ENDOFBANLIST) {
 	# +bリスト終わり
 	$got_reply->('b');
     }
-    elsif ($msg->command eq '349') {
+    elsif ($msg->command eq RPL_ENDOFEXCEPTLIST) {
 	# +eリスト終わり
 	$got_reply->('e');
     }
-    elsif ($msg->command eq '347') {
+    elsif ($msg->command eq RPL_ENDOFINVITELIST) {
 	# +Iリスト終わり
 	$got_reply->('I');
     }
diff -urN tiarra-20040122/module/Client/Cache.pm tiarra-20040214/module/Client/Cache.pm
--- tiarra-20040122/module/Client/Cache.pm	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20040214/module/Client/Cache.pm	2004-02-15 01:45:22.000000000 +0900
@@ -0,0 +1,166 @@
+# -----------------------------------------------------------------------------
+# $Id: Cache.pm,v 1.2 2004/02/14 12:16:46 topia Exp $
+# -----------------------------------------------------------------------------
+package Client::Cache;
+use strict;
+use warnings;
+use base qw(Module);
+use Mask;
+use Multicast;
+use NumericReply;
+use Configuration;
+
+sub _yesno {
+    my ($this, $value, $default) = @_;
+
+    return $default || 0 if (!defined $value);
+    return 0 if ($value =~ /[fn]/); # false/no
+    return 1 if ($value =~ /[ty]/); # true/yes
+    return 1 if ($value); # 数値判定
+    return 0;
+}
+
+sub message_arrived {
+    my ($this,$msg,$sender) = @_;
+
+    # 条件をはずれていたら last で抜ける
+    while (1) {
+	# クライアントからのメッセージか？
+	last unless ($sender->isa('IrcIO::Client'));
+	# 動作は許可されているか?
+	last unless ((!defined $sender->option('no-cache')) ||
+			 !$this->_yesno($sender->option('no-cache')));
+	if ($msg->command eq 'MODE' &&
+		$this->_yesno($this->config->use_mode_cache) &&
+		    Multicast::channel_p($msg->param(0)) &&
+			    !defined $msg->param(1)) {
+	    my $chan_long = $msg->param(0);
+	    my ($chan_short, $network_name) = Multicast::detach($chan_long);
+	    my $network = RunLoop->shared_loop->network($network_name);
+	    unless (defined $network) {
+		RunLoop->shared_loop->notify_warn(
+		    __PACKAGE__.': "'.$network_name.
+			'" network is not found in tiarra.'
+		       ) if ::debug_mode;
+		last;
+	    }
+	    my $ch = $network->channel($chan_short);
+	    last unless (defined $ch && $ch->remark('switches-are-known'));
+	    my $remark = $sender->remark('mode-cache-used') || {};
+	    if (!exists $remark->{$chan_long}) {
+		$sender->send_message(
+		    IRCMessage->new(
+			Prefix => Configuration->shared_conf->general->sysmsg_prefix,
+			Command => RPL_CHANNELMODEIS,
+			Params => [
+			    RunLoop->shared_loop->current_nick,
+			    $chan_long,
+			    $ch->mode_string,
+			   ],
+			Remarks => {
+			    'fill-prefix-when-sending-to-client' => 1,
+			},
+		       )
+		   );
+		$remark->{$chan_long} = 1;
+		$sender->remark('mode-cache-used', $remark);
+		return undef;
+	    }
+	} elsif ($msg->command eq 'WHO' &&
+		     $this->_yesno($this->config->use_who_cache) &&
+			 Multicast::channel_p($msg->param(0))) {
+	    my $chan_long = $msg->param(0);
+	    my ($chan_short, $network_name) = Multicast::detach($chan_long);
+	    my $network = RunLoop->shared_loop->network($network_name);
+	    unless (defined $network) {
+		RunLoop->shared_loop->notify_warn(
+		    __PACKAGE__.': "'.$network_name.
+			'" network is not found in tiarra.'
+		       ) if ::debug_mode;
+		last;
+	    }
+	    my $ch = $network->channel($chan_short);
+	    last unless (defined $ch);
+	    my $remark = $sender->remark('who-cache-used') || {};
+	    if (!exists $remark->{$chan_long}) {
+		# cache がそろっているかわからないため、
+		# とりあえず作ってみて、足りなかったらあきらめる。
+		my $message_tmpl = IRCMessage->new(
+		    Prefix => Configuration->shared_conf->general->sysmsg_prefix,
+		    Command => RPL_WHOREPLY,
+		    Params => [
+			RunLoop->shared_loop->current_nick,
+			$chan_long,
+		       ],
+		    Remarks => {
+			'fill-prefix-when-sending-to-client' => 1,
+		    },
+		   );
+		my @messages;
+		eval {
+		    foreach (values %{$ch->names}) {
+			my $p_ch = $_;
+			my $p = $p_ch->person;
+
+			# たいして重要でない上、
+			# 捏造が簡単なデータは捏造します。
+			# 注意してください。
+			if (!$p->username || !$p->userhost ||
+				!$p->realname || !$p->server) {
+			    # データ不足。あきらめる。
+			    RunLoop->shared_loop->notify_warn(
+				__PACKAGE__.': cache data not enough: '.$p->info.
+				    ' on '.$p->server) if ::debug_mode;
+			    die 'cache data not enough';
+			}
+
+			my $message = $message_tmpl->clone(deep => 1);
+			$message->param(2, $p->username);
+			$message->param(3, $p->userhost);
+			$message->param(4, $p->server);
+			$message->param(5, $p->nick);
+			$message->param(6,
+					(length($p->away) ? 'G' : 'H') .
+					    $p_ch->priv_symbol);
+			$message->param(7,
+					$network->remark('server-hops')
+					    ->{$p->server}.' '.
+						$p->realname);
+			push(@messages, $message);
+		    }
+		};
+		if (!$@) {
+		    my $message = $message_tmpl->clone(deep => 1);
+		    $message->command(RPL_ENDOFWHO);
+		    $message->param(2, 'End of WHO list.');
+		    push(@messages, $message);
+		    map {
+			$sender->send_message($_);
+		    } @messages;
+		    $remark->{$chan_long} = 1;
+		    $sender->remark('who-cache-used', $remark);
+		    return undef;
+		}
+	    }
+	}
+	last;
+    }
+
+    return $msg;
+}
+
+1;
+=pod
+info: データをキャッシュしてサーバに問い合わせないようにする
+default: off
+
+# キャッシュを使用しても、使われるのは接続後最初の一度だけです。
+# 二度目からは通常通りにサーバに問い合わせます。
+# また、クライアントオプションの no-cache を指定すれば動きません。
+
+# mode キャッシュを使用するか
+use-mode-cache: 1
+
+# who キャッシュを使用するか
+use-who-cache: 1
+=cut
diff -urN tiarra-20040122/module/Client/Eval.pm tiarra-20040214/module/Client/Eval.pm
--- tiarra-20040122/module/Client/Eval.pm	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20040214/module/Client/Eval.pm	2004-02-15 01:45:22.000000000 +0900
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# $Id: Eval.pm,v 1.1 2004/02/14 11:48:20 topia Exp $
+# -----------------------------------------------------------------------------
+package Client::Eval;
+use strict;
+use warnings;
+use base qw(Module);
+use Mask;
+use Data::Dumper;
+
+sub message_arrived {
+    my ($this,$msg,$sender) = @_;
+
+    # クライアントからのメッセージか？
+    if ($sender->isa('IrcIO::Client')) {
+	# 指定されたコマンドか?
+	if (Mask::match_deep([$this->config->command('all')], $msg->command)) {
+	    my ($method) = $msg->param(0);
+	    my ($ret, $err);
+	    do {
+		# disable warning
+		local $SIG{__WARN__} = sub { };
+		# die handler
+		local $SIG{__DIE__} = sub { $err = $_[0]; };
+		no strict;
+		$ret = eval($method);
+	    };
+
+	    my $message = IRCMessage->new(
+		Command => 'NOTICE',
+		Params => [RunLoop->shared_loop->current_nick,
+			   ''],
+		Remarks => {
+		    'fill-prefix-when-sending-to-client' => 1,
+		},
+	       );
+	    do {
+		local($Data::Dumper::Terse) = 1;
+		map {
+		    my $new = $message->clone;
+		    $new->param(1, $_);
+		    $sender->send_message($new);
+		} (
+		    'method: '.Dumper($method),
+		    'result: '.Dumper($ret),
+		    'error: '.$err,
+		   );
+		return undef;
+	    };
+	}
+    }
+
+    return $msg;
+}
+
+1;
+=pod
+info: クライアントから Perl 式を実行できるようにする。
+default: off
+
+# eval を実行するコマンド名。省略されるとコマンドを追加しません。
+# この時コマンドはTiarraが握り潰すので、IRCプロトコル上で定義された
+# コマンド名を設定すべきではありません。
+command: eval
+=cut
diff -urN tiarra-20040122/module/Log/Recent.pm tiarra-20040214/module/Log/Recent.pm
--- tiarra-20040122/module/Log/Recent.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Log/Recent.pm	2004-02-15 01:45:21.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Recent.pm,v 1.6 2003/11/08 06:12:56 topia Exp $
+# $Id: Recent.pm,v 1.7 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # Local: $Clovery: tiarra/module/Log/Recent.pm,v 1.5 2003/02/11 07:59:32 topia Exp $
 package Log::Recent;
@@ -11,6 +11,7 @@
 use Log::Logger;
 use IRCMessage;
 use Mask;
+use Configuration;
 
 sub new {
     my $class = shift;
@@ -44,6 +45,7 @@
     foreach my $elem (@{$this->{priv_log}}) {
 	$client->send_message(
 	    IRCMessage->new(
+		Prefix => Configuration->shared->general->sysmsg_prefix,
 		Command => 'NOTICE',
 		Params => [$local_nick,$elem->[1]])); # $elem->[0]は常に'priv'
     }
@@ -60,7 +62,7 @@
 			    $elem->[0] : $ch->name;
 		    $client->send_message(
 			IRCMessage->new(
-			    Prefix => 'Tiarra',
+			    Prefix => Configuration->shared->general->sysmsg_prefix,
 			    Command => 'NOTICE',
 			    Params => [$ch_name,$elem->[1]]));
 		}
@@ -75,7 +77,7 @@
 *C_NOTICE = \&PRIVMSG_or_NOTICE;
 sub PRIVMSG_or_NOTICE {
     my ($this,$msg,$sender) = @_;
-    my $target = Multicast::detatch($msg->param(0));
+    my $target = Multicast::detach($msg->param(0));
     my $is_priv = Multicast::nick_p($target);
     my $cmd = $msg->command;
 
@@ -130,8 +132,12 @@
 	else {
 	    # privでなければChannelInfoに'recent-log'として保存。
 	    my ($ch_short,$network_name) = Multicast::detach($ch_full);
-	    my $ch = RunLoop->shared->
-		network($network_name)->channel($ch_short);
+	    my $network = RunLoop->shared->network($network_name);
+	    if (!defined $network) {
+		RunLoop->shared->notify_warn("errorness network name: $network_name");
+		return;
+	    }
+	    my $ch = $network->channel($ch_short);
 	    if (!defined $ch) {
 		return;
 	    }
diff -urN tiarra-20040122/module/Skelton.pm tiarra-20040214/module/Skelton.pm
--- tiarra-20040122/module/Skelton.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Skelton.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Skelton.pm,v 1.1 2003/10/19 12:30:23 topia Exp $
+# $Id: Skelton.pm,v 1.2 2004/02/04 12:08:54 admin Exp $
 # -----------------------------------------------------------------------------
 # モジュールのスケルトン。
 # -----------------------------------------------------------------------------
@@ -113,12 +113,14 @@
     #         切断したサーバー。
 }
 
-sub notification_of_message_io {
+sub message_io_hook {
     my ($this,$message,$io,$type) = @_;
     # サーバーから受け取ったメッセージ、サーバーに送ったメッセージ、
     # クライアントから受け取ったメッセージ、クライアントに送ったメッセージは
-    # このメソッドで各モジュールに通知される。通知されるだけで変更する事は出来ない。
-    # 戻り値は無し。
+    # このメソッドで各モジュールに通知される。メッセージの変更も可能で、
+    # 戻り値のルールはmessage_arrivedと同じ。
+    #
+    # 通常のモジュールはこのメソッドを実装する必要は無い。
     #
     # $message :
     #    内容: IRCMessageオブジェクト
@@ -129,6 +131,7 @@
     # $type :
     #    内容: 文字列
     #         'in'なら受信、'out'なら送信
+    return $message;
 }
 
 sub control_requested {
diff -urN tiarra-20040122/module/System/Pong.pm tiarra-20040214/module/System/Pong.pm
--- tiarra-20040122/module/System/Pong.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/System/Pong.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Pong.pm,v 1.5 2003/02/13 04:21:44 topia Exp $
+# $Id: Pong.pm,v 1.6 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 # $Clovery: tiarra/module/System/Pong.pm,v 1.2 2003/02/09 02:23:58 topia Exp $
 package System::Pong;
@@ -20,7 +20,6 @@
 	# これを送りつけてきたサーバー/クライアントにPONGを送り返す。
 	$sender->send_message(
 	    new IRCMessage(
-		Prefix => $prefix,
 		Command => 'PONG',
 		Params => $message->params));
 	
diff -urN tiarra-20040122/module/System/Raw.pm tiarra-20040214/module/System/Raw.pm
--- tiarra-20040122/module/System/Raw.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/System/Raw.pm	2004-02-15 01:45:22.000000000 +0900
@@ -1,11 +1,12 @@
 # -----------------------------------------------------------------------------
-# $Id: Raw.pm,v 1.1 2003/10/15 16:23:42 admin Exp $
+# $Id: Raw.pm,v 1.2 2004/02/14 11:48:20 topia Exp $
 # -----------------------------------------------------------------------------
 package System::Raw;
 use strict;
 use warnings;
 use base qw(Module);
 use Mask;
+use Configuration;
 
 sub message_arrived {
     my ($this, $msg, $sender) = @_;
@@ -15,6 +16,7 @@
 	if ($msg->n_params < 2) {
 	    $sender->send_message(
 		IRCMessage->new(
+		    Prefix => Configuration->shared->general->sysmsg_prefix,
 		    Command => 'NOTICE',
 		    Params => [
 			RunLoop->shared->current_nick,
@@ -42,6 +44,7 @@
 	    if (!$sent) {
 		$sender->send_message(
 		    IRCMessage->new(
+			Prefix => Configuration->shared->general->sysmsg_prefix,
 			Command => 'NOTICE',
 			Params => [
 			   RunLoop->shared->current_nick, 
diff -urN tiarra-20040122/module/Tools/LinedDB.pm tiarra-20040214/module/Tools/LinedDB.pm
--- tiarra-20040122/module/Tools/LinedDB.pm	2004-01-23 04:16:12.000000000 +0900
+++ tiarra-20040214/module/Tools/LinedDB.pm	2004-02-15 01:45:22.000000000 +0900
@@ -94,7 +94,7 @@
   if (defined $this->{fpath} && $this->{fpath} ne '') {
     my $stat = stat($this->{fpath});
 
-    if ($stat->mtime > $this->{time}) {
+    if (defined($stat) && ($stat->mtime > $this->{time})) {
       $this->_load();
     }
   }
diff -urN tiarra-20040122/sample.conf tiarra-20040214/sample.conf
--- tiarra-20040122/sample.conf	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/sample.conf	2004-02-15 01:45:21.000000000 +0900
@@ -1,6 +1,6 @@
 # -*- tiarra-conf -*-
 # -----------------------------------------------------------------------------
-# $Id: sample.conf,v 1.61 2003/11/16 19:04:39 topia Exp $
+# $Id: sample.conf,v 1.62 2004/02/14 11:48:18 topia Exp $
 # -----------------------------------------------------------------------------
 # tiarra.conf サンプル
 #
@@ -140,6 +140,11 @@
   # デフォルトは、IPv4のはINADDR_ANY、IPv6のはin6addr_anyになります。
   #ipv4-bind-addr: 0.0.0.0
   #ipv6-bind-addr: ::0
+
+  # tiarra が、 001 や 002 や、 recent log を送信するときなどに使う prefix
+  # を指定します。 hostname や fqdn っぽいものを指定すると良いかもしれません。
+  # デフォルトは tiarra です。普通変える必要はありません。
+  #sysmsg-prefix: tiarra
 }
 
 # -----------------------------------------------------------------------------
@@ -778,6 +783,29 @@
   save-lists: 1
 }
 
+- Client::Eval {
+  # クライアントから Perl 式を実行できるようにする。
+
+  # eval を実行するコマンド名。省略されるとコマンドを追加しません。
+  # この時コマンドはTiarraが握り潰すので、IRCプロトコル上で定義された
+  # コマンド名を設定すべきではありません。
+  command: eval
+}
+
+- Client::Cache {
+  # データをキャッシュしてサーバに問い合わせないようにする
+
+  # キャッシュを使用しても、使われるのは接続後最初の一度だけです。
+  # 二度目からは通常通りにサーバに問い合わせます。
+  # また、クライアントオプションの no-cache を指定すれば動きません。
+  
+  # mode キャッシュを使用するか
+  use-mode-cache: 1
+  
+  # who キャッシュを使用するか
+  use-who-cache: 1
+}
+
 - Log::Channel {
   # チャンネルやprivのログを取るモジュール。
 
diff -urN tiarra-20040122/tiarra tiarra-20040214/tiarra
--- tiarra-20040122/tiarra	2004-01-23 04:16:11.000000000 +0900
+++ tiarra-20040214/tiarra	2004-02-15 01:45:21.000000000 +0900
@@ -1,11 +1,11 @@
 #!/usr/bin/perl
 # -----------------------------------------------------------------------------
 # - T i a r r a - :::bootstrap:::
-# Copyright (c) 2002-2003 phonohawk. All rights reserved.
+# Copyright (c) 2002-2004 phonohawk. All rights reserved.
 # This is free software; you can redistribute it and/or modify it
 #   under the same terms as Perl itself.
 # -----------------------------------------------------------------------------
-# $Id: tiarra,v 1.31 2004/01/22 19:13:45 topia Exp $
+# $Id: tiarra,v 1.32 2004/02/14 16:40:34 admin Exp $
 # -----------------------------------------------------------------------------
 require 5.006;
 use strict;
@@ -296,7 +296,7 @@
 		 ("- T i a r r a - :::version ${version}:::",
 		  "                    based #${based_version}")
 	    ),
-	    "Copyright (c) 2002-2003 phonohawk. All rights reserved.",
+	    "Copyright (c) 2002-2004 phonohawk. All rights reserved.",
 	    "This is free software; you can redistribute it and/or modify it",
 	    "  under the same terms as Perl itself.");
 }
