3 # $Id: keitairc,v 1.3 2004-03-21 11:01:33 morimoto Exp $
5 # Copyright (c) 2003 Jun Morimoto <morimoto@xantia.citroen.org>
6 # This program is covered by the GNU General Public License 2
8 # Depends: libjcode-pm-perl, libpoe-component-irc-perl,
9 # liburi-perl, libwww-perl, libappconfig-perl
14 use POE::Component::Server::TCP;
15 use POE::Component::IRC;
16 use POE::Filter::HTTPD;
19 use AppConfig qw(:argcount);
21 my $config = AppConfig->new(
25 ARGCOUNT => ARGCOUNT_ONE,
28 qw(irc_nick irc_username irc_desc
29 irc_server irc_port irc_password
30 web_port web_title web_lines
31 web_username web_password)
34 unless($config->file($ENV{'HOME'} . '/.keitairc')){
35 $config->file('/etc/keitairc');
39 my (%channels, %buffer, %atime, %mtime);
42 POE::Component::IRC->new('keitairc');
44 _start => \&on_irc_start,
45 irc_join => \&on_irc_join,
46 irc_part => \&on_irc_part,
47 irc_public => \&on_irc_public,
48 irc_notice => \&on_irc_notice,
51 # web server component
52 POE::Component::Server::TCP->new(
54 Port => $config->web_port,
55 ClientFilter => 'POE::Filter::HTTPD',
56 ClientInput => \&on_web_request
62 ################################################################
64 my $kernel = $_[KERNEL];
65 $kernel->post('keitairc' => 'register' => 'all');
66 $kernel->post('keitairc' => 'connect' => {
67 Nick => $config->irc_nick,
68 Username => $config->irc_username,
69 Ircname => $config->irc_desc,
70 Server => $config->irc_server,
71 Port => $config->irc_port,
72 Password => $config->irc_password
76 ################################################################
78 my ($kernel, $who, $channel) = @_[KERNEL, ARG0, ARG1];
79 $channel = Jcode->new($channel)->euc;
80 $channels{$channel}++;
83 ################################################################
85 my ($kernel, $who, $channel) = @_[KERNEL, ARG0, ARG1];
86 $channel = Jcode->new($channel->[0])->euc;
87 delete $channels{$channel};
90 ################################################################
92 my ($kernel, $who, $channel, $msg) = @_[KERNEL, ARG0 .. ARG2];
94 $channel = $channel->[0];
95 $msg = Jcode->new($msg)->euc;
96 &add_message($channel, $who, $msg);
99 ################################################################
101 my ($kernel, $who, $channel, $msg) = @_[KERNEL, ARG0 .. ARG2];
103 $channel = $channel->[0];
104 $msg = Jcode->new($msg)->euc;
105 &add_message($channel, $who, $msg);
108 ################################################################
109 # $msg
\e$B$O
\e(B EUC
\e$B$K$J$C$F$$$k$O$:
\e(B
110 # $channel
\e$B$O
\e(B jis
\e$B$G$-$F$k$>
\e(B
112 my($channel, $who, $msg) = @_;
114 $channel = Jcode->new($channel)->euc;
116 my @tmp = split("\n", $buffer{$channel});
117 push @tmp, sprintf('%s %s> %s', &now, $who, $msg);
119 if(@tmp > $config->web_lines){
120 $buffer{$channel} = join("\n", splice(@tmp, -$config->web_lines));
122 $buffer{$channel} = join("\n", @tmp);
125 $mtime{$channel} = time;
128 ################################################################
130 my ($sec,$min,$hour) = localtime(time);
131 sprintf('%02d:%02d', $hour, $min);
134 ################################################################
143 ################################################################
148 for my $channel (sort(keys(%channels))){
151 $buf .= sprintf('&#%d;', 63878 + $accesskey);
156 unless(defined($buffer{$channel})){
157 $buf .= &compact_channel_name($channel);
160 $buf .= sprintf('<a accesskey="%1d" href="/%s">%s</a>',
162 uri_escape($channel),
163 &compact_channel_name($channel));
165 $buf .= sprintf(' <a href="/%s">%s</a>',
166 uri_escape($channel),
167 &compact_channel_name($channel));
173 if($atime{$channel} < $mtime{$channel}){
180 $buf .= '<a href="/" accesskey="0">';
185 ################################################################
186 sub compact_channel_name{
195 ################################################################
200 my @src = (reverse(split("\n", shift)))[0 .. $config->web_lines];
208 unless(s,(http://[!-;=-\177]+),<a href="$1">$1</a>,g){
209 unless(s|(www\.[!-\177]+)|<A HREF="http://$1">$1</A>|g){
211 unless(s|(0\d{1,3}[-(]?\d{2,4}[-)]?\d{4})|<a href="tel:$1">$1</a>|g){
212 s|(\w[\w.+=-]+\@[\w.-]+[\w])|<a href="mailto:$1">$1</a>|g;
222 '<pre>' . join("\n", @buf) . '</pre>';
225 ################################################################
227 my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0];
229 # Filter::HTTPD sometimes generates HTTP::Response objects.
230 # They indicate (and contain the response for) errors that occur
231 # while parsing the client's HTTP request. It's easiest to send
232 # the responses as they are and finish up.
233 if($request->isa('HTTP::Response')){
234 $heap->{client}->put($request);
235 $kernel->yield('shutdown');
239 if(defined($config->web_username)){
240 unless($request->headers->authorization_basic eq
241 $config->web_username . ':' . $config->web_password){
243 my $response = HTTP::Response->new(401);
244 $response->push_header(WWW_Authenticate =>
245 qq(Basic Realm="keitairc"));
246 $heap->{client}->put($response);
247 $kernel->yield('shutdown');
252 my $uri = $request->uri;
253 my $content = '<html><head>';
255 # POST
\e$B$5$l$F$-$?$b$N$OH/8@
\e(B
256 if($request->method =~ /POST/i){
257 my $message = $request->content;
259 $message =~ s/\+/ /g;
260 $message = uri_unescape($message);
262 if(length($message)){
264 my $channel = uri_unescape($uri);
265 $poe_kernel->post('keitairc',
267 Jcode->new($channel)->jis,
268 Jcode->new($message)->jis);
269 &add_message($channel, $config->irc_nick, $message);
274 $content .= '<title>' . $config->web_title . '</title>';
275 $content .= '<body>';
276 $content .= &index_page;
279 my $channel = uri_unescape($uri);
281 $content .= '<title>' . $config->web_title . ': $channel</title>';
282 $content .= '<body>';
284 $content .= '<a name="1"></a>';
285 $content .= '<a accesskey="7" href="#1"></a>';
287 $content .= sprintf('<form action="/%s" method="post">', $uri);
288 $content .= '<input type="text" name="m" size="10">';
289 $content .= '<input type="submit" accesskey="1" value="聆">';
290 $content .= '</form>';
292 if(defined($channels{$channel})){
293 if(defined($buffer{$channel}) &&
294 length($buffer{$channel})){
295 $content .= '<a accesskey="8" href="/"></a>';
296 $content .= '<a accesskey="9" href="#2"></a>';
297 $content .= &render($buffer{$channel});
298 $content .= '<a name="2"></a>';
300 $content .= 'no message here yet';
303 $content .= "no such channel";
306 $atime{$channel} = time;
309 $content .= '</body></html>';
311 my $response = HTTP::Response->new(200);
312 $response->push_header('Content-type', 'text/html; charset=Shift_JIS');
313 $response->content(Jcode->new($content)->sjis);
314 $heap->{client}->put($response);
315 $kernel->yield('shutdown');