diff -urN tiarra-20031019/ChangeLog tiarra-20031024/ChangeLog
--- tiarra-20031019/ChangeLog	2003-10-19 19:48:01.000000000 +0900
+++ tiarra-20031024/ChangeLog	2003-11-08 04:38:31.000000000 +0900
@@ -1,3 +1,32 @@
+2003-10-25  phonohawk  <phonohawk@lycos.co.uk>
+
+	* main/IRCMessage.pm (serialize):
+	最後のパラメータが空文字列だった場合、コロンを残さない為に
+	シリアライズ後のパラメタが減ってしまう問題を解決。
+
+	* main/IrcIO/Server.pm (_receive_while_logging_in):
+	サーバーがERRORを返した時、その内容でdieするように。
+
+2003-10-24  phonohawk  <phonohawk@lycos.co.uk>
+
+	* main/Configuration/Block.pm:
+	$block->foo_bar('block') とした時、戻り値が常にブロックとなる。
+	未定義であれば空のブロックを、既定義かつ値がブロックであれば
+	そのブロックを、既定義かつ値がブロックでなければキーと値のペアを
+	一つだけ含むブロックを生成して返す。
+
+2003-10-19  Topia  <topia@clovery.jp>
+
+	* HACKING:
+	モジュール作成者向けのドキュメントを書いた。
+	まだ、Timer / Hook / Socket I/O 関連の記述がない。
+
+	* module/Skelton.pm:
+	新規モジュールのスケルトン。中身のほとんどは main/Module.pm と同一。
+
+	* module/Auto/Alias.pm (message_arrived):
+	返り値がおかしかったのを修正。
+
 2003-10-19  phonohawk  <phonohawk@lycos.co.uk>
 
 	* main/IrcIO/Server.pm
@@ -979,7 +1008,7 @@
 
 	* これ以前のログは書いていません。
 
-#       Id: $Id: ChangeLog,v 1.109 2003/10/19 10:41:17 admin Exp $
+#       Id: $Id: ChangeLog,v 1.113 2003/10/24 15:57:48 admin Exp $
 #   Author: $Author: admin $
-#     Date: $Date: 2003/10/19 10:41:17 $
-# Revision: $Revision: 1.109 $
+#     Date: $Date: 2003/10/24 15:57:48 $
+# Revision: $Revision: 1.113 $
diff -urN tiarra-20031019/HACKING tiarra-20031024/HACKING
--- tiarra-20031019/HACKING	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20031024/HACKING	2003-11-08 04:38:31.000000000 +0900
@@ -0,0 +1,48 @@
+モジュールについて
+
+* モジュールの新規作成
+module/Skelton.pm にモジュールのスケルトンがありますので、
+これをコピーして不要な関数を削除すれば、作ることが出来ます。
+
+* 注意すべき事項
+  - Tiarra はシングルスレッドです。時間がかかる処理をそのままやってはいけません。
+    Timer や Hook, ソケット入出力なら ExternalSocket を使って少しずつ処理してください。
+
+* 良く使うモジュール/関数
+  - Mask(main/Mask.pm)
+    汎用に使えるマスクマッチング関数群が実装してあります。
+    良く使う形は
+      Mask::match_deep_chan([$this->config->mask('all')], $msg->prefix, $channel_name_with_network)
+      Mask::match_deep([$this->config->keyword('all')], $keyword)
+    だと思います。
+
+  - Multicast(main/Multicast.pm)
+    + detach($str)	文字列 $str からネットワーク名を外す。
+      戻り値: (セパレータ前の文字列,ネットワーク名,ネットワーク名が明示されたかどうか)
+      ただしスカラーコンテクストではセパレータ前の文字列のみを返す。
+    + attach($str, $network_name)	$str にネットワーク名を付ける。
+      $strはChannelInfoのオブジェクトでも良い。
+      $network_nameは省略可能。IrcIO::Serverのオブジェクトでも良い。
+    + nick_p($str)	文字列 $str が nick name として許される形式なら 1 を返す。
+      ネットワーク名は付けたままでも構わない。処理前に detach される。
+    + channel_p($str)	文字列 $str が channel name として許される形式なら 1 を返す。
+      ネットワーク名は付けたままでも構わない。処理前に detach される。
+
+  - RunLoop(main/RunLoop.pm)
+    ここで紹介する関数は、全て RunLoop->shared_loop->function(...) と呼んで下さい。
+    + channel($str)	チャンネルを探す。
+      ネットワーク名付きのチャンネル名が引数です。無ければ undef を返します。
+    + broadcast_to_clients(@messages)	 メッセージを全てのクライアントに送信する。
+    * notify_msg($str)		全てのクライアントと、 STDOUT にメッセージを通知します。
+    + notify_error($str)	notify_msg を使ってエラーを通知します。
+    + notify_warn($str)		notify_msg を使って警告を通知します。
+
+  - main(tiarra)
+    + ::printmsg()		STDOUT にのみメッセージを通知します。
+    + ::debug_printmsg()	デバッグモードの時のみメッセージを通知します。
+    + ::debug_mode()		デバッグモードなら 1 を返します。
+    + ::ipv6_enabled()		IPv6 が有効なら 1 を返します。
+
+  - Auto::Utils(module/Auto/Utils.pm)
+    + generate_reply_closures(...)	一般的な自動反応をするのに有用なクロージャを生成する。
+      一般的な使い方は Skelton.pm に書いておきました。
diff -urN tiarra-20031019/main/Configuration/Block.pm tiarra-20031024/main/Configuration/Block.pm
--- tiarra-20031019/main/Configuration/Block.pm	2003-10-19 19:48:01.000000000 +0900
+++ tiarra-20031024/main/Configuration/Block.pm	2003-11-08 04:38:31.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Block.pm,v 1.9 2003/09/20 11:06:18 admin Exp $
+# $Id: Block.pm,v 1.10 2003/10/24 06:12:29 admin Exp $
 # -----------------------------------------------------------------------------
 package Configuration::Block;
 use strict;
@@ -26,6 +26,13 @@
 # パラメータfooに複数の定義があれば、そのうちの一つをランダムに返す。
 # 一つも無ければundefを返す。
 #
+# $block->foo_bar('block');
+# $block->get('foo-bar', 'block');
+# パラメータ"foo-bar"の値が未定義である場合、undef値の代わりに
+# 空のConfiguration::Blockを返す。
+# 定義されている場合、その値がブロックであればそれを返すが、
+# そうでなければ "foo-bar: その値" の要素を持ったブロックを生成し、それを返す。
+#
 # $block->get('foo_bar');
 # $block->get('foo_bar','all');
 # パラメータ"foo_bar"の値を返す。
@@ -137,7 +144,10 @@
 	# そのような値は定義されていない。
 	if ($option && $option eq 'all') {
 	    return ();
-	}	
+	}
+	elsif ($option and $option eq 'block') {
+	    return Configuration::Block->new($key);
+	}
 	else {
 	    return undef;
 	}
@@ -164,6 +174,16 @@
 	    return $this->eval_code($value);
 	}
     }
+    elsif ($option and $option eq 'block') {
+	if (ref($value) and UNIVERSAL::isa($value, 'Configuration::Block')) {
+	    return $value;
+	}
+	else {
+	    my $tmp_block = Configuration::Block->new($key);
+	    $tmp_block->set($key, $value);
+	    return $tmp_block;
+	}
+    }
     else {
 	if (ref($value) eq 'ARRAY') {
 	    return $this->eval_code($value->[0]); # 配列リファなら先頭の値を返す。
diff -urN tiarra-20031019/main/IRCMessage.pm tiarra-20031024/main/IRCMessage.pm
--- tiarra-20031019/main/IRCMessage.pm	2003-10-19 19:48:01.000000000 +0900
+++ tiarra-20031024/main/IRCMessage.pm	2003-11-08 04:38:31.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: IRCMessage.pm,v 1.14 2003/10/12 12:08:57 admin Exp $
+# $Id: IRCMessage.pm,v 1.15 2003/10/24 15:52:29 admin Exp $
 # -----------------------------------------------------------------------------
 # IRCMessageはIRCのメッセージを表わすクラスです。実際のメッセージはUTF-8で保持します。
 # 生のメッセージのパース、シリアライズ、そしてメッセージの生成をサポートします。
@@ -241,9 +241,11 @@
 	    if ($i == $n_params - 1) {
 		# 最後のパラメタなら頭にコロンを付けて後にはスペースを置かない。
 		# 但し半角スペースが一つも無く、且つコロンで始まっていなければコロンを付けない。
+		# パラメタが空文字列であった場合は例外としてコロンを付ける。
 		my $arg = $unicode->set($this->[PARAMS]->[$i])->conv($encoding);
-		if (index($arg, ' ') == -1 and
-		      index($arg, ':') != 0) {
+		if (length($arg) > 0 and
+		      index($arg, ' ') == -1 and
+			index($arg, ':') != 0) {
 		    $result .= $arg;
 		}
 		else {
diff -urN tiarra-20031019/main/IrcIO/Server.pm tiarra-20031024/main/IrcIO/Server.pm
--- tiarra-20031019/main/IrcIO/Server.pm	2003-10-19 19:48:01.000000000 +0900
+++ tiarra-20031024/main/IrcIO/Server.pm	2003-11-08 04:38:31.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Server.pm,v 1.48 2003/10/19 10:41:16 admin Exp $
+# $Id: Server.pm,v 1.49 2003/10/24 15:52:29 admin Exp $
 # -----------------------------------------------------------------------------
 # IrcIO::ServerはIRCサーバーに接続し、IRCメッセージをやり取りするクラスです。
 # このクラスはサーバーからメッセージを受け取ってチャンネル情報や現在のnickなどを保持しますが、
@@ -394,8 +394,8 @@
     }
     else {
 	# それ以外。手の打ちようがないのでconnectionごと切断してしまう。
-	# 但し、ニューメリックリプライでなければ無視する。
-	if ($reply =~ m/^\d+/) {
+	# 但し、ニューメリックリプライでもERRORでもなければ無視する。
+	if ($reply eq 'ERROR' or $reply =~ m/^\d+/) {
 	    $this->disconnect;
 	    die "Server replied $reply.\n".$first_msg->serialize."\n";
 	}
diff -urN tiarra-20031019/module/Auto/Alias.pm tiarra-20031024/module/Auto/Alias.pm
--- tiarra-20031019/module/Auto/Alias.pm	2003-10-19 19:48:02.000000000 +0900
+++ tiarra-20031024/module/Auto/Alias.pm	2003-11-08 04:38:32.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Alias.pm,v 1.8 2003/07/31 07:34:13 topia Exp $
+# $Id: Alias.pm,v 1.9 2003/10/19 12:30:23 topia Exp $
 # -----------------------------------------------------------------------------
 # $Clovery: tiarra/module/Auto/Alias.pm,v 1.13 2003/07/27 07:17:07 topia Exp $
 package Auto::Alias;
@@ -75,7 +75,7 @@
       }
     }
   }
-  return $msg;
+  return @result;
 }
 
 1;
diff -urN tiarra-20031019/module/Channel/Freeze.pm tiarra-20031024/module/Channel/Freeze.pm
--- tiarra-20031019/module/Channel/Freeze.pm	2003-10-19 19:48:03.000000000 +0900
+++ tiarra-20031024/module/Channel/Freeze.pm	2003-11-08 04:38:33.000000000 +0900
@@ -1,5 +1,5 @@
 # -----------------------------------------------------------------------------
-# $Id: Freeze.pm,v 1.2 2003/07/23 07:25:29 admin Exp $
+# $Id: Freeze.pm,v 1.3 2003/10/19 12:32:19 admin Exp $
 # -----------------------------------------------------------------------------
 # このモジュールは再起動しても凍結設定を失はないやうにする爲、
 # 設定をBulletinBoardのfrost-channelsに保存します。
@@ -23,7 +23,7 @@
     $this;
 }
 
-sub DESTROY {
+sub destruct {
     my $this = shift;
     if (defined $this->{reminder_timer}) {
 	$this->{reminder_timer}->uninstall;
diff -urN tiarra-20031019/module/Skelton.pm tiarra-20031024/module/Skelton.pm
--- tiarra-20031019/module/Skelton.pm	1970-01-01 09:00:00.000000000 +0900
+++ tiarra-20031024/module/Skelton.pm	2003-11-08 04:38:33.000000000 +0900
@@ -0,0 +1,145 @@
+# -----------------------------------------------------------------------------
+# $Id: Skelton.pm,v 1.1 2003/10/19 12:30:23 topia Exp $
+# -----------------------------------------------------------------------------
+# モジュールのスケルトン。
+# -----------------------------------------------------------------------------
+package Skelton;
+use strict;
+use warnings;
+use base qw(Module);
+
+sub new {
+    my $class = shift;
+    # モジュールが必要になった時に呼ばれる。
+    # これはモジュールのコンストラクタである。
+    # 引数は無し。
+    my $this = $class->SUPER::new;
+
+    return $this;
+}
+
+sub destruct {
+    my $this = shift;
+    # モジュールが不要になった時に呼ばれる。
+    # これはモジュールのデストラクタである。このメソッドが呼ばれた後はDESTROYを除いて
+    # いかなるメソッドも呼ばれる事が無い。タイマーを登録した場合は、このメソッドが
+    # 責任を持ってそれを解除しなければならない。
+    # 引数は無し。
+}
+
+sub message_arrived {
+    my ($this,$message,$sender) = @_;
+    # サーバーまたはクライアントからメッセージが来た時に呼ばれる。
+    # 戻り値はIRCMessageまたはその配列またはundef。
+    #
+    # $message :
+    #    内容: IRCMessageオブジェクト
+    #    サーバーから、またはクライアントから送られてきたメッセージ。
+    #    モジュールはこのオブジェクトをそのまま返しても良いし、
+    #    改変して返しても良いし何も返さなくても良いし二つ以上返しても良い。
+    # $sender :
+    #    内容: IrcIOオブジェクト
+    #    このメッセージを発したIrcIO。サーバーまたはクライアントである。
+    #    メッセージがサーバーから来たのかクライアントから来たのかは
+    #    $sender->isa('IrcIO::Server')などとすれば判定出来る。
+    #
+    # サーバー→クライアントの流れでも、Prefixを持たないメッセージを
+    # 流しても構わない。逆に言えば、そのようなメッセージが来ても
+    # 問題が起こらないようにモジュールを設計しなければならない。
+    return $message;
+}
+## Auto::Utils::generate_reply_closures を使う場合。
+# sub message_arrived {
+#     my ($this,$message,$sender) = @_;
+#     my @result = ($msg);
+# 
+#     if ($msg->command eq 'PRIVMSG') {
+# 	my ($reply,$reply_as_priv,$get_raw_ch_name,$reply_anywhere,$get_full_ch_name)
+# 	    = Auto::Utils::generate_reply_closures($msg,$sender,\@result);
+# 
+# 	$reply_anywhere->('Hello, #(name|default_name)',
+# 			'default_name' => '(your name)');
+# 	if ($get_raw_ch_name->() eq '#Tiarra_testing') {
+# 	    # なんらかの処理
+# 	}
+# 	if ($get_full_ch_name->() eq '#Tiarra_testing@LocalServer') {
+# 	    # なんらかの処理
+# 	}
+#     }
+#     return @result;
+# }
+# 
+
+sub client_attached {
+    my ($this,$client) = @_;
+    # クライアントが新規に接続した時に呼ばれる。
+    # 戻り値は無し。
+    #
+    # $client :
+    #    内容: IrcIO::Clientオブジェクト
+    #    接続されたクライアント。
+}
+
+sub client_detached {
+    my ($this,$client) = @_;
+    # クライアントが切断した時に呼ばれる。
+    # 戻り値は無し。
+    #
+    # $client :
+    #    内容: IrcIO::Clientオブジェクト
+    #    切断したクライアント。
+}
+
+sub connected_to_server {
+    my ($this,$server,$new_connection) = @_;
+    # サーバーに接続した時に呼ばれる。
+    # 戻り値は無し。
+    #
+    # $server :
+    #    内容: IrcIO::Serverオブジェクト
+    #         接続したサーバー。
+    # $new_connection :
+    #    内容: 真偽値
+    #         新規の接続なら1。切断後の自動接続ではundef。
+}
+
+sub disconnected_from_server {
+    my ($this,$server) = @_;
+    # サーバーから切断した(或いはされた)時に呼ばれる。
+    # 戻り値は無し。
+    #
+    # $server :
+    #    内容: IrcIO::Serverオブジェクト
+    #         切断したサーバー。
+}
+
+sub notification_of_message_io {
+    my ($this,$message,$io,$type) = @_;
+    # サーバーから受け取ったメッセージ、サーバーに送ったメッセージ、
+    # クライアントから受け取ったメッセージ、クライアントに送ったメッセージは
+    # このメソッドで各モジュールに通知される。通知されるだけで変更する事は出来ない。
+    # 戻り値は無し。
+    #
+    # $message :
+    #    内容: IRCMessageオブジェクト
+    #         送受信されたメッセージ
+    # $io :
+    #    内容: IrcIO::Server又はIrcIO::Clientオブジェクト
+    #         送受信が行なわれたIrcIO
+    # $type :
+    #    内容: 文字列
+    #         'in'なら受信、'out'なら送信
+}
+
+sub control_requested {
+    my ($this,$request) = @_;
+    # 外部コントロールプログラムからのメッセージが来た。
+    # 戻り値はControlPort::Reply。
+    #
+    # $request:
+    #    内容 : ControlPort::Request
+    #          送られたリクエスト
+    die "This module doesn't support controlling.\n";
+}
+
+1;
