--- ../temp/vendors/keitairc_yukinon-1.29+y9	2005-05-05 00:30:23.000000000 +0900
+++ keitairc	2005-06-08 09:09:53.000000000 +0900
@@ -1,4 +1,4 @@
-#!/usr/local/bin/perl
+#!/usr/bin/env perl
 # keitairc
 # $Id: keitairc,v 1.29 2004/09/16 13:44:10 morimoto Exp $
 #
@@ -25,24 +25,18 @@
 # $Id$
 #
 
-# utf8 uri + use Unicode::Japanese
+# utf8 uri
 # Copyright of Changes (c) 2004 Topia <topia@clovery.jp>
 #
 # $Id$
 #
 
-# - use Unicode::Japanese
-# Copyright of Changes (c) 2004 HIIRAGI Yukio <yukio@tls>
-#
-# $Id$
-#
-
 my $rcsid = q$Id: keitairc,v 1.29 2004/09/16 13:44:10 morimoto Exp $;
 my ($version) = $rcsid =~ m#,v ([0-9.]+)#;
 
 # yukinon version
 $version .= "+y9";
-$version .= "+t3";
+$version .= "+t6";
 
 use strict;
 use Jcode;
@@ -56,32 +50,38 @@
 
 use constant true => 1;
 use constant false => 0;
-use constant cookie_ttl => 86400*3;  # 3 days
+use constant cookie_ttl => 86400*3; # 3 days
 
 my $config = AppConfig->new(
-			    {
-				CASE => 1,
-				GLOBAL => {
-				    ARGCOUNT => ARGCOUNT_ONE,
-				}
-			    },
-			    qw(irc_nick irc_username irc_desc
-			       irc_server irc_port irc_password
-			       au_subscriber_id use_cookie
-			       web_port web_title web_lines web_root
-			       web_username web_password show_newmsgonly
-			       web_input_encoding uri_prefix)
-			    );
+	{
+	    CASE => 1,
+	    GLOBAL => {
+		ARGCOUNT => ARGCOUNT_ONE,
+	    }
+	   },
+	qw(irc_nick irc_username irc_desc
+	   irc_server irc_port irc_password
+	   au_subscriber_id use_cookie
+	   web_port web_title web_lines web_root
+	   web_username web_password show_newmsgonly
+	   web_input_encoding uri_prefix buf_lines
+	   net_sep)
+       );
 
 $config->file('/etc/keitairc') if ( -e '/etc/keitairc');
 $config->file($ENV{'HOME'} . '/.keitairc');
 $config->args;
 
 my $docroot = '/';
-if(defined $config->web_root){
+if (defined $config->web_root) {
     $docroot = $config->web_root;
 }
 
+my $buf_lines = $config->buf_lines;
+if (!defined $buf_lines) {
+    $buf_lines = $config->web_lines;
+}
+
 # join $B$7$F$$$k%A%c%M%k$NL>>N$r5-O?$9$k%O%C%7%e(B
 my %channel_name;
 
@@ -91,14 +91,14 @@
 # $B%A%c%M%k$N2qOCFbMF$r5-O?$9$k%O%C%7%e(B
 my (%channel_buffer, %channel_recent);
 
-# $B3F%A%c%M%k$N:G=*%"%/%;%9;~9o!":G?7H/8@;~9o(B
-my (%atime, %mtime);
+# $B3F%A%c%M%k$N:G?7H/8@;~9o(B
+my (%mtime);
 
 # unread lines
 my %unread;
 
 # chk
-my ($send_chk, $update_chk);
+my ($message_added);
 
 # $B%A%c%s%M%k%f!<%6!<$r5-O?$9$k%O%C%7%e(B
 my %users_list;
@@ -111,29 +111,29 @@
 # irc component
 POE::Component::IRC->new('keitairc');
 POE::Session->new(
-		  _start => \&on_irc_start,
-		  irc_join => \&on_irc_join,
-		  irc_part => \&on_irc_part,
-		  irc_public => \&on_irc_public,
-		  irc_notice => \&on_irc_notice,
-		  irc_topic => \&on_irc_topic,
- 		  irc_332 => \&on_irc_topicraw,
-		  irc_ctcp_action => \&on_irc_ctcp_action,
-		  irc_quit => \&on_irc_quit,
-		  irc_nick => \&on_irc_nick,
-		  irc_msg => \&on_irc_msg,
-		  irc_353 => \&on_irc_rpl_namreply,
-		  irc_366 => \&on_irc_rpl_endofnames,
-		  irc_disconnected => \&on_irc_disconnected
-		  );
+    _start => \&on_irc_start,
+    irc_join => \&on_irc_join,
+    irc_part => \&on_irc_part,
+    irc_public => \&on_irc_public,
+    irc_notice => \&on_irc_notice,
+    irc_topic => \&on_irc_topic,
+    irc_332 => \&on_irc_topicraw,
+    irc_ctcp_action => \&on_irc_ctcp_action,
+    irc_quit => \&on_irc_quit,
+    irc_nick => \&on_irc_nick,
+    irc_msg => \&on_irc_msg,
+    irc_353 => \&on_irc_rpl_namreply,
+    irc_366 => \&on_irc_rpl_endofnames,
+    irc_disconnected => \&on_irc_disconnected
+   );
 
 # web server component
 POE::Component::Server::TCP->new(
-				 Alias => 'keitairc',
-				 Port => $config->web_port,
-				 ClientFilter => 'POE::Filter::HTTPD',
-				 ClientInput => \&on_web_request
-				 );
+    Alias => 'keitairc',
+    Port => $config->web_port,
+    ClientFilter => 'POE::Filter::HTTPD',
+    ClientInput => \&on_web_request
+   );
 
 $poe_kernel->run();
 exit 0;
@@ -150,7 +150,7 @@
 	Server => $config->irc_server,
 	Port => $config->irc_port,
 	Password => $config->irc_password
-	});
+       });
 
     $channel_name{$console}++;
     $channel_name{$private}++;
@@ -187,7 +187,6 @@
 	delete $channel_name{$channel};
 	delete $unread{$channel};
 	delete $mtime{$channel};
-	delete $atime{$channel};
 	delete $topic{$channel};
 	delete $users_list{$channel};
 	delete $channel_buffer{$channel};
@@ -222,7 +221,7 @@
     $who =~ s/!.*//;
     $channel = $channel->[0];
     $msg = Jcode->new($msg, 'jis')->euc;
-    &add_message($channel, $who, $msg);
+    &add_message($channel, "$who>", $msg);
 }
 
 
@@ -232,7 +231,7 @@
     $who =~ s/!.*//;
     $channel = $channel->[0];
     $msg = Jcode->new($msg, 'jis')->euc;
-    &add_message($channel, $who, $msg);
+    &add_message($channel, "$who)", $msg);
 }
 
 
@@ -273,7 +272,7 @@
     }
 
     for my $channel (sort keys %channel_name) {
-#	$users_list{$channel} =~ s/\b$who\b/$nick/g
+	#$users_list{$channel} =~ s/\b$who\b/$nick/g
 	&rename_users_list($channel, $who, $nick);
     }
 
@@ -286,7 +285,7 @@
     $who =~ s/!.*//;
     $channel = $channel->[0];
     $msg = Jcode->new($msg, 'jis')->euc;
-    &add_message($channel, $who, $msg);
+    &add_message($channel, "$who>", $msg);
 }
 
 
@@ -295,10 +294,7 @@
     my ($kernel, $raw) = @_[KERNEL, ARG1];
     my ($channel, $names) = split(/ :/, $raw, 2);
 
-#    $channel = Jcode->new(&compact_channel_name($channel), 'jis')->euc;
-#    printf("rpl_namreply raw = %s\n", $raw);
-#    printf("rpl_namreply = %s\n", $channel);
-
+    $users_list_work .= ' ' unless $users_list_work =~ / $/;
     $users_list_work .= $names;
 }
 
@@ -307,12 +303,10 @@
     my ($kernel, $raw) = @_[KERNEL, ARG1];
     my ($channel, $names) = split(/ :/, $raw, 2);
 
-#    $channel = Jcode->new(&compact_channel_name($channel), 'jis')->euc;
-#    printf("endifnames raw = %s\n", $raw);
-#    printf("endofnames = %s\n", $channel);
-
     $channel = unification_channel_name($channel);
 
+    $users_list_work =~ s/(?<!\S)[+@]/$1/g;
+
     $users_list{$channel} = $users_list_work;
     $users_list_work = '';
 }
@@ -325,7 +319,7 @@
     my $who = Jcode->new($config->web_title, 'jis')->euc;
 
     for my $channel (sort keys %channel_name) {
-	&add_message($channel, $who, "Disconnected Server : $server");
+	&add_message($channel, undef, "Disconnected Server : $server");
     }
 }
 
@@ -352,14 +346,16 @@
 	$channel = $private;
     }
 
+    $channel = unification_channel_name($channel);
+
     # remove color change code
     $msg =~ s/\x03(\d\d(,\d\d)?)?//g;
 
     my $message;
-    if(length $who){
-      $message = sprintf('%s %s> %s', &now, $who, $msg);
-    }else{
-      $message = sprintf('%s %s', &now, $msg);
+    if (length $who) {
+	$message = sprintf('%s %s %s', &now, $who, $msg);
+    } else {
+	$message = sprintf('%s %s', &now, $msg);
     }
 
     my @tmp = split("\n", $channel_buffer{$channel});
@@ -371,21 +367,21 @@
     # unread lines
     $unread{$channel} = scalar(@tmp2);
 
-    if ($unread{$channel} > $config->web_lines) {
-	$unread{$channel} = $config->web_lines;
+    if ($unread{$channel} > $buf_lines) {
+	$unread{$channel} = $buf_lines;
     }
 
-    if(@tmp > $config->web_lines){
+    if (@tmp > $buf_lines) {
 	$channel_buffer{$channel} =
-		join("\n", splice(@tmp, -$config->web_lines));
-    }else{
+	    join("\n", splice(@tmp, -$buf_lines));
+    } else {
 	$channel_buffer{$channel} = join("\n", @tmp);
     }
 
-    if(@tmp2 > $config->web_lines){
+    if (@tmp2 > $buf_lines) {
 	$channel_recent{$channel} =
-		join("\n", splice(@tmp2, -$config->web_lines));
-    }else{
+	    join("\n", splice(@tmp2, -$buf_lines));
+    } else {
 	$channel_recent{$channel} = join("\n", @tmp2);
     }
 
@@ -427,9 +423,9 @@
 sub label{
     my $accesskey = shift;
 
-    if($accesskey < 10){
+    if ($accesskey < 10) {
 	sprintf('%d ', $accesskey);
-    }else{
+    } else {
 	'  ';
     }
 }
@@ -441,38 +437,40 @@
 
     for my $channel (sort {
 	$mtime{$b} <=> $mtime{$a};
-    }(keys(%channel_name))){
+    }(keys(%channel_name))) {
 
 	$buf .= &label($accesskey);
 
-	if($accesskey < 10){
-		$buf .= sprintf('<a accesskey="%1d" href="%s%s">%s</a>',
-				$accesskey,
-				$docroot,
-				&channel_to_uri($channel),
-				Jcode->new(&compact_channel_name($channel), 'jis')->euc);
-	}else{
-		$buf .= sprintf('<a href="%s%s">%s</a>',
-				$docroot,
-				&channel_to_uri($channel),
-				Jcode->new(&compact_channel_name($channel), 'jis')->euc);
+	if ($accesskey < 10) {
+	    $buf .= sprintf('<a accesskey="%1d" href="%s%s">%s</a>',
+			    $accesskey,
+			    $docroot,
+			    &channel_to_uri($channel),
+			    Jcode->new(&compact_channel_name($channel), 'jis')->euc);
+	} else {
+	    $buf .= sprintf('<a href="%s%s">%s</a>',
+			    $docroot,
+			    &channel_to_uri($channel),
+			    Jcode->new(&compact_channel_name($channel), 'jis')->euc);
 	}
 
 	$accesskey++;
 
 	# $BL$FI9T?t(B
-	if($unread{$channel} > 0){
-		$buf .= sprintf(' <a href="%s%s.update">%d</a>',
-				$docroot,
-				&channel_to_uri($channel),
-				$unread{$channel});
+	if ($unread{$channel} > 0) {
+	    $buf .= sprintf(' <a href="%s%s,recent">%d</a>',
+			    $docroot,
+			    &channel_to_uri($channel),
+			    $unread{$channel});
 	}
 	$buf .= '<br>';
     }
 
     $buf .= qq(0 <a href="$docroot" accesskey="0">refresh</a><br>);
-    $buf .= qq(* <a href="$docroot.topics" accesskey="*">Topic list</a><br>);
-    $buf .= qq(# <a href="$docroot.recent" accesskey="#">Recent list</a><br>);
+    $buf .= qq(* <a href="$docroot,recent" accesskey="*">recent</a><br>);
+    if (%topic) {
+	$buf .= qq(# <a href="$docroot,topics" accesskey="#">topics</a><br>);
+    }
     $buf .= qq( - keitairc $version);
     $buf;
 }
@@ -486,62 +484,74 @@
 
     for my $channel (sort {
 	$mtime{$b} <=> $mtime{$a};
-    }(keys(%channel_name))){
+    }(keys(%channel_name))) {
 	if ($unread{$channel} > 0) {
-            $buf .= '<p>';
-            $buf .= sprintf('<a href="%s%s">%s</a><br>',
-				$docroot,
-				&channel_to_uri($channel),
-				Jcode->new(&compact_channel_name($channel), 'jis')->euc);
+	    $buf .= '<p>';
+	    $buf .= sprintf('<a href="%s%s">%s</a><br>',
+			    $docroot,
+			    &channel_to_uri($channel),
+			    Jcode->new(&compact_channel_name($channel), 'jis')->euc);
 	    $buf .= '<font size="-1">' if ($mobile);
 	    $buf .= &render($channel_recent{$channel}, false);
 	    $buf .= '</font>' if ($mobile);
+	    $buf .= '</p>';
 	}
-	$buf .= '</p>';
 
 	# clear unread counter
-        $unread{$channel} = 0;
+	$unread{$channel} = 0;
 
 	# clear recent messages buffer
 	$channel_recent{$channel} = '';
     }
 
-    $buf .= qq(0 <a accesskey="0" href="$docroot.recent">refresh</a><br>);
-    $buf .= qq(* <a accesskey="*" href="$docroot.topics">Topic list</a><br>);
-    $buf .= qq(# <a accesskey="#" href="$docroot">Channel list</a><br>);
+    $buf .= qq(0 <a accesskey="0" href="$docroot,recent">refresh</a><br>);
+    $buf .= qq(* <a accesskey="*" href="$docroot">Channel list</a><br>);
+    $buf .= qq(# <a accesskey="#" href="$docroot,topics">topics</a><br>);
 
     return $buf;
 }
 
 
 #################################################################
-sub index_topic {
+sub channel_topic {
+    my $channel = shift;
     my $mobile = shift;
     my $buf;
 
     $buf = '';
 
-    for my $channel (sort keys %channel_name) {
-	$buf .= '<p>';
-	$buf .= sprintf('<a href="%s%s">%s</a><br>',
-				$docroot,
-				&channel_to_uri($channel),
-				Jcode->new(&compact_channel_name($channel), 'jis')->euc);
-	$buf .= '<font size="-1">' if ($mobile);
-	$buf .= &escape(Jcode->new($topic{$channel}, 'jis')->euc);
+    $buf .= '<p>';
+    $buf .= sprintf('<a href="%s%s">%s</a><br>',
+		    $docroot,
+		    &channel_to_uri($channel),
+		    Jcode->new(&compact_channel_name($channel), 'jis')->euc);
+    $buf .= '<font size="-1">' if ($mobile);
+    $buf .= &escape(Jcode->new($topic{$channel}, 'jis')->euc);
+    $buf .= '<br>';
 
-	for my $user (sort split(' ', $users_list{$channel})) {
-	    $buf .= $user;
-	    $buf .= '<br>';
-	}
+    for my $user (sort split(' ', $users_list{$channel})) {
+	$buf .= $user;
+	$buf .= '<br>';
+    }
 
-	$buf .= '</font>' if ($mobile);
-	$buf .= '</p>';
+    $buf .= '</font>' if ($mobile);
+    $buf .= '</p>';
+}
+
+#################################################################
+sub index_topic {
+    my $mobile = shift;
+    my $buf;
+
+    $buf = '';
+
+    for my $channel (sort keys %channel_name) {
+	$buf .= channel_topic($channel, $mobile);
     }
 
-    $buf .= qq(0 <a accesskey="0" href="$docroot.topics">refresh</a><br>);
-    $buf .= qq(# <a accesskey="#" href="$docroot.recent">Recent list</a><br>);
-    $buf .= qq(* <a accesskey="*" href="$docroot">Channel list</a><br>);
+    $buf .= qq(0 <a accesskey="0" href="$docroot,topics">refresh</a><br>);
+    $buf .= qq(* <a accesskey="*" href="$docroot,recent">recent</a><br>);
+    $buf .= qq(# <a accesskey="#" href="$docroot">Channel list</a><br>);
 
     return $buf;
 }
@@ -554,16 +564,15 @@
     for my $channel (sort keys %users_list) {
 	$buf .= '<p>';
 	$buf .= sprintf('<a href="%s%s">%s</a><br>',
-				$docroot,
-				&channel_to_uri($channel),
-				Jcode->new(&compact_channel_name($channel), 'jis')->euc);
+			$docroot,
+			&channel_to_uri($channel),
+			Jcode->new(&compact_channel_name($channel), 'jis')->euc);
 	$buf .= '<font size="-1">' if ($mobile);
 
 	for my $user (sort split(' ', $users_list{$channel})) {
 	    $buf .= $user;
 	    $buf .= '<br>';
 	}
-#	$buf .= $users_list{$channel};
 	$buf .= '</p>';
     }
 
@@ -586,14 +595,14 @@
 			   Jcode->new(compact_channel_name($channel), 'jis')->euc);
 	# for my $message ( split("\n", $channel_buffer{$channel}) ) {
 	for my $message ( split("\n", $channel_recent{$channel}) ) {
-		push (@tmp, &escape($message) . " ($link)<br>\n" );
+	    push (@tmp, &escape($message) . " ($link)<br>\n" );
 	}
     }
     my $current_n = 0;
     for my $message (sort {$b cmp $a;} @tmp) {
-        $buf .= $message;
-        $current_n++;
-        last if $current_n > $recent_n;
+	$buf .= $message;
+	$current_n++;
+	last if $current_n > $recent_n;
     }
     return $buf;
 }
@@ -603,8 +612,13 @@
 sub compact_channel_name{
     local($_) = shift;
 
+    if (defined $config->net_sep) {
+	my $suf = quotemeta($config->net_sep);
+	s/$suf[^$suf]*?($|:)/$1/o;
+    }
+
     # #name:*.jp $B$r(B %name $B$K(B
-    if(s/:\*\.jp$//){
+    if (s/:\*\.jp$//) {
 	s/^#/%/;
     }
 
@@ -616,41 +630,37 @@
 
 ################################################################
 sub render{
-    my($message, $reverse) = @_;
-#   local($_);
+    my($message, $reverse, $page) = @_;
     my @buf;
-    my @src;
 
-#   my @src = (reverse(split("\n", shift)))[0 .. $config->web_lines];
-    if ($reverse) {
-	@src = (reverse(split("\n", shift)))[0 .. $config->web_lines];
-    } else {
-	@src = (split("\n", shift))[0 .. $config->web_lines];
-    }
+    #   my @src = (reverse(split("\n", shift)))[0 .. $config->web_lines];
+    my @src = (reverse(split("\n", $message)))[$page * $config->web_lines ..
+						   ($page+1) * $config->web_lines-1];;
+    @src = reverse(@src) unless $reverse;
     my $uri_prefix = defined $config->uri_prefix ? $config->uri_prefix : '';
 
-    for (@src){
+    for (@src) {
 	next unless defined;
 	next unless length;
-	
+
 	$_ = &escape($_);
-	
-	unless(s,\b(https?://[!-;=-\177]+)\b,<a href="${uri_prefix}$1">$1</a>,g){
-	    unless(s|\b(www\.[!-\177]+)\b|<a href="${uri_prefix}http://$1">$1</a>|g){
+
+	unless (s,\b(https?://[!-;=-\177]+)\b,<a href="${uri_prefix}$1">$1</a>,g) {
+	    unless (s|\b(www\.[!-\177]+)\b|<a href="${uri_prefix}http://$1">$1</a>|g) {
 		# phone to
-		unless(s|\b(0\d{1,3})([-(]?)(\d{2,4})([-)]?)(\d{4})\b|<a href="tel:$1$3$5">$1$2$3$4$5</a>|g){
+		unless (s|\b(0\d{1,3})([-(]?)(\d{2,4})([-)]?)(\d{4})\b|<a href="tel:$1$3$5">$1$2$3$4$5</a>|g) {
 		    s|\b(\w[\w.+=-]+\@[\w.-]+[\w]\.[\w]{2,4})\b|<a href="mailto:$1">$1</a>|g;
 		}
 	    }
 	}
-	
+
 	s/\s+$//;
 	s/\s+/ /g;
 	$_  .= '<br>';
 	push @buf, $_;
     }
 
-#    '<pre>' . join("\n", @buf) . '</pre>';
+    #    '<pre>' . join("\n", @buf) . '</pre>';
 
     join("\n", @buf);
 }
@@ -663,7 +673,7 @@
     # They indicate (and contain the response for) errors that occur
     # while parsing the client's HTTP request.  It's easiest to send
     # the responses as they are and finish up.
-    if($request->isa('HTTP::Response')){
+    if ($request->isa('HTTP::Response')) {
 	$heap->{client}->put($request);
 	$kernel->yield('shutdown');
 	return;
@@ -671,7 +681,7 @@
 
     my $mobile;
     $mobile = 0;
-    if ($request->user_agent =~ /DoCoMo/) {
+    if ($request->user_agent =~ /(DoCoMo|UP\.Browser|J-PHONE)/) {
 	$mobile = 1;
     }
 
@@ -679,245 +689,239 @@
 	$mobile = 2;
     }
 
-#    # cookie $B<hF@(B
-#    my %COOKIE;
-#    my $ccusername = '';
-#    my $ccpasswd = '';
-#    my $cookie_auth = 0;
-#    my $name;
-#    my $value;
-#    my $xx;
-#    my $cookie = $request->header('Cookie');
-#    my @val;
-#    foreach $xx (split(/; */,$cookie)) {
-#	($name, $value) = split(/=/, $xx);
-#        $value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C", hex($1))/eg;
-#        $COOKIE{$name} = $value;
-#    }
-#    
-#    if ($COOKIE{'username'} ne '') {
-#		$ccusername = $COOKIE{'username'};
-#    }
-#    if ($COOKIE{'passwd'} ne '') {
-# 	$ccpasswd = $COOKIE{'passwd'};
-#	if ($ccusername eq $config->web_username && $ccpasswd eq $config->web_password) {
-#		$cookie_auth = 1;
-#	} else {
-#		$cookie_auth = 0;
-#	}
-#    }
-#
-#    if(defined($config->web_username) && $cookie_auth eq 0){
-#	unless($request->headers->authorization_basic eq
-#	       $config->web_username . ':' . $config->web_password){
-#
-#	    my $response = HTTP::Response->new(401);
-#	    $response->push_header(WWW_Authenticate =>
-#				   qq(Basic Realm="keitairc"));
-#	    $heap->{client}->put($response);
-#	    $kernel->yield('shutdown');
-#	    return;
-#	}
-#    }
-#
-#    $ccusername = $config->web_username;
-#    $ccpasswd   = $config->web_password;
-
     # cookie
     my $cookie_authorized;
-    if($config->use_cookie){
+    if ($config->use_cookie) {
 	my %cookie;
-	for(split(/; */, $request->header('Cookie'))){
-		my ($name, $value) = split(/=/);
-		$value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('C', hex($1))/eg;
-        	$cookie{$name} = $value;
-	}
-	if($cookie{username} eq $config->web_username && $cookie{passwd} eq $config->web_password){
-		$cookie_authorized = true;
+	for (split(/; */, $request->header('Cookie'))) {
+	    my ($name, $value) = split(/=/);
+	    $value =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('C', hex($1))/eg;
+	    $cookie{$name} = $value;
+	}
+	if ($cookie{username} eq $config->web_username &&
+		$cookie{passwd} eq $config->web_password) {
+	    $cookie_authorized = true;
 	}
     }
 
     # authorization
     unless($cookie_authorized){
-	unless(defined($config->au_subscriber_id) && $request->header('x-up-subno') eq $config->au_subscriber_id){
-		if(defined($config->web_username)){
-			unless($request->headers->authorization_basic eq $config->web_username . ':' . $config->web_password){
-				my $response = HTTP::Response->new(401);
-				$response->push_header(WWW_Authenticate => qq(Basic realm="keitairc"));
-				$heap->{client}->put($response);
-				$kernel->yield('shutdown');
-				return;
-			}
+	unless(defined($config->au_subscriber_id) &&
+		   $request->header('x-up-subno') eq $config->au_subscriber_id){
+	    if (defined($config->web_username)) {
+		unless($request->headers->authorization_basic eq
+			   $config->web_username . ':' . $config->web_password){
+		    my $response = HTTP::Response->new(401);
+		    $response->push_header(WWW_Authenticate => qq(Basic realm="keitairc"));
+		    $heap->{client}->put($response);
+		    $kernel->yield('shutdown');
+		    return;
 		}
+	    }
 	}
     }
 
     my $uri = $request->uri;
     my $content = '';
     $content .= '<html><head>';
-#   $content .= '<meta http-equiv="Cache-Control" content="no-cache" />';
+    #   $content .= '<meta http-equiv="Cache-Control" content="no-cache" />';
     $content .= '<meta http-equiv="Cache-Control" content="max-age=0" />';
 
+    # store and remove attached options from uri
+    my %option;
+    while ($uri =~ s/,(.+?)(=.*)?$//) {
+	if (defined $2) {
+	    $option{$1} = $2;
+	    $option{$1} =~ s/^=//;
+	} else {
+	    $option{$1} = $1;
+	}
+    }
+
+    $uri =~ s|^/||;
+
     # POST $B$5$l$F$-$?$b$N$OH/8@(B
-    if($request->method =~ /POST/i){
+    if ($request->method =~ /POST/i) {
 	my $message = $request->content;
 	$message =~ s/^m=//;
 	$message =~ s/\+/ /g;
 	$message = uri_unescape($message);
 
-	if(length($message)){
-	    $uri =~ s|^/||;
+	if (length($message)) {
 	    my $channel = &uri_to_channel($uri);
-	    if ($message =~ /^\//) {
-		$message =~ s/\///;
-		my @postcmd = split(/ /, $message);
-		$poe_kernel->post('keitairc',
-			      Jcode->new(@postcmd[0], $config->web_input_encoding)->jis,
-			      Jcode->new(@postcmd[1], $config->web_input_encoding)->jis);
+	    if ($message =~ s|^/||) {
+		my ($params, $trailing) = split(/ :/, $message, 2);
+		my @postcmd = split(/ /, $params);
+		push @postcmd, $trailing if defined $trailing;
+
+		$poe_kernel->post('keitairc', map {
+		    Jcode->new($_, $config->web_input_encoding)->jis
+		} @postcmd);
 	    } else {
 		$poe_kernel->post('keitairc',
-			      'privmsg',
-			      Jcode->new($channel, $config->web_input_encoding)->jis,
-			      Jcode->new($message, $config->web_input_encoding)->jis);
-		&add_message($channel, $config->irc_nick,
+				  'privmsg',
+				  Jcode->new($channel, $config->web_input_encoding)->jis,
+				  Jcode->new($message, $config->web_input_encoding)->jis);
+		&add_message($channel, $config->irc_nick . '>',
 			     Jcode->new($message, $config->web_input_encoding)->euc);
+		$message_added = true;
 	    }
 	}
     }
 
-    if($uri eq '/'){
-	$content .= '<title>' . $config->web_title . '</title>';
-	$content .= '</head>';
-	$content .= '<body>';
-	$content .= &index_page;
-    } elsif ($uri =~ /.*.recent/) {
-	$content .= '<title>' . $config->web_title . '</title>';
-	$content .= '</head>';
-	$content .= '<body>';
-	$content .= &index_recent($mobile);
-    } elsif ($uri =~ /.*.topics/) {
-	$content .= '<title>' . $config->web_title . '</title>';
-	$content .= '</head>';
-	$content .= '<body>';
-	$content .= &index_topic($mobile);
-    } elsif ($uri =~ /.*.users/) {
+    my $page = $option{p} || 0;
+
+    if ($uri eq '') {
 	$content .= '<title>' . $config->web_title . '</title>';
 	$content .= '</head>';
 	$content .= '<body>';
-	$content .= &index_users($mobile);
-    }else{
-	$uri =~ s|^/||;
 
-	$update_chk = ($uri =~ /.*.update/);
-	if ($update_chk eq 1) {
-		$uri =~ s/.update//;
+	if ($option{recent}) {
+	    $content .= &index_recent($mobile);
+	} elsif ($option{topics}) {
+	    $content .= &index_topic($mobile);
+	} elsif ($option{users}) {
+	    $content .= &index_users($mobile);
+	} else {
+	    $content .= &index_page;
 	}
+    } else {
+	# RFC 2811:
+	# Apart from the the requirement that the first character
+	# being either '&', '#', '+' or '!' (hereafter called "channel
+	# prefix"). The only restriction on a channel name is that it
+	# SHALL NOT contain any spaces (' '), a control G (^G or ASCII
+	# 7), a comma (',' which is used as a list item separator by
+	# the protocol).  Also, a colon (':') is used as a delimiter
+	# for the channel mask.  The exact syntax of a channel name is
+	# defined in "IRC Server Protocol" [IRC-SERVER].
+	#
+	# so we use white space as separator character of channel name
+	# and command argument.
 
-	my $channel = &uri_to_channel($uri);
+	my $channel = uri_to_channel($uri);
 
 	$content .= '<title>' . $config->web_title . ": " .
 	    Jcode->new($channel, 'jis')->euc . "</title>";
 	$content .= '</head>';
 	$content .= '<body>';
 
-	$content .= '<a name="1"></a>';
-	$content .= '<a accesskey="7" href="#1"></a>';
+	if ($option{topic}) {
+	    if (defined($channel_name{$channel})) {
+		$content .= channel_topic($channel, $mobile);
+		$content .= sprintf('<a accesskey="5" href="%s%s">back[5]</a>',
+				    "$docroot", &channel_to_uri($channel));
+	    } else {
+		$content .= "no such channel";
+	    }
+	} else {
+	    $content .= '<a name="1"></a>';
+	    $content .= '<a accesskey="7" href="#1"></a>';
+
+	    $content .= sprintf('<form action="%s%s" method="post">',
+				$docroot, &channel_to_uri($channel));
+
+	    my @tmp = split("\n", $channel_buffer{$channel});
+	    my ($goback, $goforward) = ('', '');
+	    my $channel_uri = channel_to_uri($channel);
+	    if (0 < $page) {
+		my $pp = $page - 1;
+		$goback .= qq(<a accesskey="4" href="${docroot}${channel_uri},p=${pp}">[4]<=</a>);
+	    }
+	    if (($page+1) * $config->web_lines < @tmp) {
+		my $pp = $page + 1;
+		$goforward .= qq(<a accesskey="6" href="${docroot}${channel_uri},p=${pp}">=>[6]</a>);
+	    }
 
-	$content .= sprintf('<form action="%s%s" method="post">',
-			    $docroot, &channel_to_uri($channel));
-	if ($mobile) {
+	    if ($mobile) {
 		$content .= '<input type="text" name="m" size="22">' if ($mobile == 1);
 		$content .= '<input type="text" name="m" size="15">' if ($mobile == 2);
-		$content .= '<input type="submit" accesskey="1" value="OK">';
-		$content .= qq(<a accesskey="8" href="$docroot">Ch</a><br>);
-	} else {
+		$content .= '<input type="submit" accesskey="1" value="OK[1]"><br>';
+		if ($goback ne '' or $goforward ne '') {
+		    $content .= ' '. $goback . '('.$page.')'. $goforward . ' ';
+		}
+		$content .= qq(<a accesskey="8" href="$docroot">Ch[8]</a>);
+	    } else {
 		$content .= '<input type="text" name="m" size="64">';
-		$content .= '<input type="submit" accesskey="1" value="OK">';
-	        $content .= qq(<a accesskey="8" href="$docroot">list</a><br>);
-	}
-	$content .= '</form>';
-
-	$content .= '<font size ="-1">' if ($mobile);
-
-	if(defined($channel_name{$channel})){
-	    if(defined($channel_buffer{$channel}) && length($channel_buffer{$channel})) {
-		$content .= '<a accesskey="9" href="#2"></a>';
-		if ((($update_chk eq 1)||((defined $config->show_newmsgonly) && ($send_chk eq 1)))) {
-		    $content .= &render($channel_recent{$channel}, true);
-		    $content .= sprintf('<a accesskey="5" href="%s%s">
-			..more[5]</a>', "$docroot", &channel_to_uri($channel));
+		$content .= '<input type="submit" accesskey="1" value="OK[1]"><br>';
+		if ($goback ne '' or $goforward ne '') {
+		    $content .= ' '. $goback . '('.$page.')'. $goforward . ' ';
+		}
+		$content .= qq(<a accesskey="8" href="$docroot">list[8]</a>);
+	    }
+	    $content .= sprintf(',<a href="%s%s,topic">topic</a>',
+				$docroot, &channel_to_uri($channel));
+	    $content .= '<br></form>';
+
+	    $content .= '<font size ="-1">' if ($mobile);
+
+	    if (defined($channel_name{$channel})) {
+		if (defined($channel_buffer{$channel}) && length($channel_buffer{$channel})) {
+		    $content .= '<a accesskey="9" href="#2"></a>';
+		    if ($option{recent} ||
+			    (defined($config->show_newmsgonly) && $message_added)) {
+			$content .= &render($channel_recent{$channel}, true, $page);
+			$content .= sprintf('<a accesskey="5" href="%s%s">...more[5]</a>',
+					    $docroot, &channel_to_uri($channel));
+		    } else {
+			$content .= &render($channel_buffer{$channel}, true, $page);
+		    }
+		    $content .= '<a name="2"></a>';
 		} else {
-		  $content .= &render($channel_buffer{$channel}, true);
+		    $content .= 'no message here yet';
 		}
-		$content .= '<a name="2"></a>';
-	    }else{
-		$content .= 'no message here yet';
+	    } else {
+		$content .= "no such channel";
 	    }
-	}else{
-	    $content .= "no such channel";
-	}
 
-        # add recent messages in all channels
-	if (($update_chk ne 1) && ($send_chk ne 1)) {
-	    $content .= '<hr>';
-	    $content .= recent_all_messages($channel, 10);
-	}
+	    # add recent messages in all channels
+	    if (!defined $option{update}) {
+		$content .= '<hr>';
+		$content .= recent_all_messages($channel, 10);
+	    }
 
-	# clear check flags
-	$send_chk = 0;
+	    # clear check flags
+	    $message_added = false;
 
-	# clear unread counter
-        $unread{$channel} = 0;
+	    # clear unread counter
+	    $unread{$channel} = 0;
 
-	# clear recent messages buffer
-	$channel_recent{$channel} = '';
+	    # clear recent messages buffer
+	    $channel_recent{$channel} = '';
 
-	# mobile mode end
-	$content .= '</font>' if ($mobile);
+	    # mobile mode end
+	    $content .= '</font>' if ($mobile);
+	}
 
 	# add channel list link
 	$content .= '<hr>';
-	$content .= qq(# <a accesskey="#" href="$docroot.recent">Recent list</a><br>);
-	$content .= qq(* <a accesskey="*" href="$docroot.topics">Topic list</a><br>);
+	$content .= qq(* <a accesskey="*" href="$docroot,recent">recent</a><br>);
+	$content .= qq(# <a accesskey="#" href="$docroot,topics">topics</a><br>);
 	$content .= qq(<a href="$docroot">Channel List</a><br>);
-
-	$atime{$channel} = time;
     }
 
     $content .= '</body></html>';
 
-#    # cookie$B5-O?(B
-#    my @Months = ('','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
-#    my @WeekDays =('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
-#    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time + cookie_ttl);
-#    $mon++;
-#    $year+=1900;
-#    
-#    my $response = HTTP::Response->new(200);
-#    my $expire = sprintf("%.3s, %.2d-%.3s-%.4s %.2d:%.2d:%.2d",$WeekDays[$wday],$mday,$Months[$mon],$year,$hour,$min,$sec);
-#    $response->push_header('Set-Cookie',"username=$ccusername; expires=$expire; \n");
-#    $response->push_header('Set-Cookie',"passwd=$ccpasswd; expires=$expire; \n");
-
     my $response = HTTP::Response->new(200);
 
     if ($config->use_cookie) {
-      my ($sec, $min, $hour, $mday, $mon, $year, $wday) =
-	localtime(time + cookie_ttl);
-      my $expiration =
-	sprintf('%.3s, %.2d-%.3s-%.4s %.2d:%.2d:%.2d',
-		qw(Sun Mon Tue Wed Thu Fri Sat)[$wday],
-		$mday,
-		qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$mon],
-		$year + 1900,
-		$hour,
-		$min,
-		$sec);
-      $response->push_header('Set-Cookie',
-			     sprintf("username=%s; expires=%s; \n",
-				     $config->web_username, $expiration));
-      $response->push_header('Set-Cookie',
-			     sprintf("passwd=%s; expires=%s; \n",
-				     $config->web_password, $expiration));
+	my ($sec, $min, $hour, $mday, $mon, $year, $wday) =
+	    localtime(time + cookie_ttl);
+	my $expiration =
+	    sprintf('%.3s, %.2d-%.3s-%.4s %.2d:%.2d:%.2d',
+		    qw(Sun Mon Tue Wed Thu Fri Sat)[$wday],
+		    $mday,
+		    qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$mon],
+		    $year + 1900,
+		    $hour,
+		    $min,
+		    $sec);
+	$response->push_header('Set-Cookie',
+			       sprintf("username=%s; expires=%s; \n",
+				       $config->web_username, $expiration));
+	$response->push_header('Set-Cookie',
+			       sprintf("passwd=%s; expires=%s; \n",
+				       $config->web_password, $expiration));
     }
 
     $response->push_header('Content-type', 'text/html; charset=Shift_JIS');
@@ -973,7 +977,6 @@
     my @new = ();
     for (my $i = 0; $i <= $#org; $i++) {
 	my $name = $org[$i];
-	$name =~ s/@//g;
 	if ($name ne $who) {
 	    push(@new, $org[$i]);
 	}
@@ -990,7 +993,6 @@
 
     for (my $i = 0; $i < $#org; $i++) {
 	my $name = $org[$i];
-	$name =~ s/@//g;
 	if ($name eq $who) {
 	    $org[$i] = $nick;
 	}
