#!/usr/bin/perl
+# -*- mode: perl; coding: utf-8 -*-
# keitairc
-# $Id: keitairc,v 1.13 2004-03-21 11:04:37 morimoto Exp $
#
-# Copyright (c) 2003 Jun Morimoto <morimoto@xantia.citroen.org>
+# Copyright (c) 2003-2010 Jun Morimoto <morimoto@mrmt.net>
# This program is covered by the GNU General Public License 2
#
-# Depends: libjcode-pm-perl, libpoe-component-irc-perl,
-# liburi-perl, libwww-perl, libappconfig-perl
-
-my $rcsid = q$Id: keitairc,v 1.13 2004-03-21 11:04:37 morimoto Exp $;
-my ($version) = $rcsid =~ m#,v ([0-9.]+)#;
+# Depends: libpoe-component-irc-perl,
+# liburi-perl, libwww-perl, libappconfig-perl, libproc-daemon-perl,
+# libhtml-template-perl
+#
+# 00location_receiver plugin use XML::Simple, so if you want to use it
+# Depends: libxml-simple-perl
-use strict;
-use Jcode;
+use Encode;
use POE;
-use POE::Component::Server::TCP;
use POE::Filter::HTTPD;
use POE::Component::IRC;
+use POE::Component::Server::TCP;
use URI::Escape;
+use HTML::Template;
use HTTP::Response;
-use AppConfig qw(:argcount);
-
-my $config = AppConfig->new(
- {
- CASE => 1,
- GLOBAL => {
- ARGCOUNT => ARGCOUNT_ONE,
- }
- },
- qw(irc_nick irc_username irc_desc
- irc_server irc_port irc_password
- web_port web_title web_lines web_root
- web_username web_password)
- );
-
-$config->file('/etc/keitairc');
-$config->file($ENV{'HOME'} . '/.keitairc');
-$config->args;
-
-my $docroot = '/';
-if(defined $config->web_root){
- $docroot = $config->web_root;
+use HTTP::Status;
+
+use FindBin;
+use lib ("$FindBin::Bin/lib", '/usr/share/keitairc/lib');
+use Keitairc::Config;
+use Keitairc::View;
+use Keitairc::IrcBuffer;
+use Keitairc::IrcCallback;
+use Keitairc::ClientInfo;
+use Keitairc::SessionManager;
+use Keitairc::Plugins;
+use Keitairc::Log;
+use strict;
+use warnings;
+
+our $cf = new Keitairc::Config({version => '2.1a1', argv => \@ARGV});
+
+# daemonize
+if($cf->daemonize()){
+ if (eval 'require Proc::Daemon') {
+ require Proc::Daemon;
+ Proc::Daemon::Init();
+ if(length $cf->pid_dir()){
+ if (open(PID, '> ' . $cf->pid_dir() . '/' . $cf->pid_file())) {
+ print PID $$, "\n";
+ close(PID);
+ }
+ }
+ $poe_kernel->has_forked if ($poe_kernel->can('has_forked'));
+ } else {
+ warn('Proc::Daemon module is not installed, could not daemonize');
+ }
}
-# join \e$B$7$F$$$k%A%c%M%k$NL>>N$r5-O?$9$k%O%C%7%e\e(B
-my %channel_name;
-
-# \e$B%A%c%M%k$N2qOCFbMF$r5-O?$9$k%O%C%7%e\e(B
-my (%channel_buffer, %channel_recent);
-
-# \e$B3F%A%c%M%k$N:G=*%"%/%;%9;~9o!":G?7H/8@;~9o\e(B
-my %mtime;
-
-# unread lines
-my %unread;
-
-# chk
-my ($send_chk, $update_chk);
-
-# 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,
- );
-
-# web server component
+our $log = new Keitairc::Log({config => $cf});
+our $ib = new Keitairc::IrcBuffer({history => $cf->web_lines()});
+our $sm = new Keitairc::SessionManager({default_ttl => $cf->session_ttl()});
+our $pl = new Keitairc::Plugins({config => $cf});
+
+# create irc component
+our $irc = POE::Component::IRC->spawn(
+ Alias => 'keitairc_irc',
+ Nick => $cf->irc_nick(),
+ Username => $cf->irc_username(),
+ Ircname => $cf->irc_desc(),
+ Server => $cf->irc_server(),
+ Port => $cf->irc_port(),
+ Password => $cf->irc_password());
+
+# create POE session
+POE::Session->create(
+ heap => {
+ seen_traffic => 0,
+ disconnect_msg => 1,
+ Config => $cf,
+ Irc => $irc,
+ IrcBuffer => $ib,
+ },
+ inline_states => {
+ _start => \&Keitairc::IrcCallback::irc_start,
+ autoping => \&Keitairc::IrcCallback::irc_autoping,
+ connect => \&Keitairc::IrcCallback::irc_connect,
+ irc_registered => \&Keitairc::IrcCallback::irc_registered,
+ irc_001 => \&Keitairc::IrcCallback::irc_001,
+ irc_join => \&Keitairc::IrcCallback::irc_join,
+ irc_part => \&Keitairc::IrcCallback::irc_part,
+ irc_quit => \&Keitairc::IrcCallback::irc_quit,
+ irc_public => \&Keitairc::IrcCallback::irc_public,
+ irc_notice => \&Keitairc::IrcCallback::irc_notice,
+ irc_mode => \&Keitairc::IrcCallback::irc_mode,
+ irc_nick => \&Keitairc::IrcCallback::irc_nick,
+ irc_msg => \&Keitairc::IrcCallback::irc_msg,
+ irc_topic => \&Keitairc::IrcCallback::irc_topic,
+ irc_332 => \&Keitairc::IrcCallback::irc_topicraw,
+ irc_352 => \&Keitairc::IrcCallback::irc_whoreply,
+ irc_ctcp_action => \&Keitairc::IrcCallback::irc_ctcp_action,
+ irc_disconnected => \&Keitairc::IrcCallback::irc_reconnect,
+ irc_error => \&Keitairc::IrcCallback::irc_reconnect,
+ irc_socketerr => \&Keitairc::IrcCallback::irc_reconnect,
+ });
+
+# create 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 => $cf->web_listen_port(),
+ ClientFilter => 'POE::Filter::HTTPD',
+ ClientInput => \&http_request);
+# fire up main loop
$poe_kernel->run();
exit 0;
################################################################
-sub on_irc_start{
- my $kernel = $_[KERNEL];
- $kernel->post('keitairc' => 'register' => 'all');
- $kernel->post('keitairc' => 'connect' => {
- Nick => $config->irc_nick,
- Username => $config->irc_username,
- Ircname => $config->irc_desc,
- Server => $config->irc_server,
- Port => $config->irc_port,
- Password => $config->irc_password
- });
-}
-
-################################################################
-sub on_irc_join{
- my ($kernel, $who, $channel) = @_[KERNEL, ARG0, ARG1];
- $channel_name{$channel}++;
-}
-
-################################################################
-sub on_irc_part{
- my ($kernel, $who, $channel) = @_[KERNEL, ARG0, ARG1];
- $who =~ s/!.*//;
-
- # chop off after the gap (bug workaround of POE::Filter::IRC)
- $channel =~ s/ .*//;
-
- if ($who eq $config->irc_nick) {
- delete $channel_name{$channel};
- } else {
- &add_message($channel, 'SYSOP', $who . ' leaves');
- }
-}
-
-################################################################
-sub on_irc_public{
- my ($kernel, $who, $channel, $msg) = @_[KERNEL, ARG0 .. ARG2];
- $who =~ s/!.*//;
- $channel = $channel->[0];
- $msg = Jcode->new($msg, 'jis')->euc;
- &add_message($channel, $who, $msg);
-}
+sub http_request{
+ my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0];
+
+ # Filter::HTTPD sometimes generates HTTP::Response objects.
+ # 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')){
+ $heap->{client}->put($request);
+ $log->log_error($request->as_string());
+ }elsif(my $response = dispatch($request)){
+ $heap->{client}->put($response);
+ $log->log_access($heap->{'remote_ip'}, $request, $response);
+ }
-################################################################
-sub on_irc_notice{
- my ($kernel, $who, $channel, $msg) = @_[KERNEL, ARG0 .. ARG2];
- $who =~ s/!.*//;
- $channel = $channel->[0];
- $msg = Jcode->new($msg, 'jis')->euc;
- &add_message($channel, $who, $msg);
+ $kernel->yield('shutdown');
}
################################################################
-# $msg \e$B$O\e(B EUC \e$B$K$J$C$F$$$k$O$:\e(B
-# $channel \e$B$O\e(B jis \e$B$G$-$F$k$>\e(B
-sub add_message{
- my($channel, $who, $msg) = @_;
+sub dispatch{
+ my $request = shift;
+ my $uri = $request->uri();
+ my $ci = new Keitairc::ClientInfo($request);
- my $message = sprintf('%s %s> %s', &now, $who, $msg);
+ $log->log_debug("dispatch: $uri");
- my @tmp = split("\n", $channel_buffer{$channel});
- push @tmp, $message;
+ {
+ # chop off $cf->web_root()
+ my $root = $cf->web_root();
+ $uri =~ s|$root|/|;
+ }
- my @tmp2 = split("\n", $channel_recent{$channel});
- push @tmp2, $message;
+ if($uri eq '/'){
+ return action_root($request);
+ }
- if(@tmp > $config->web_lines){
- $channel_buffer{$channel} =
- join("\n", splice(@tmp, -$config->web_lines));
- }else{
- $channel_buffer{$channel} = join("\n", @tmp);
- }
+ if($uri eq '/login'){
+ return action_login($request);
+ }
- if(@tmp2 > $config->web_lines){
- $channel_recent{$channel} =
- join("\n", splice(@tmp2, -$config->web_lines));
- }else{
- $channel_recent{$channel} = join("\n", @tmp2);
- }
+ if($uri eq '/login_icc'){
+ return action_login_icc($request);
+ }
- $mtime{$channel} = time;
+ if($uri eq '/login_imodeid?guid=ON'){
+ return action_login_imodeid($request);
+ }
- # unread lines
- $unread{$channel} = @tmp2;
+ for my $name ($pl->list_action_plugins()){
+ if($uri =~ m|^/(S[a-zA-Z]{10})/$name/(.*)| ||
+ $uri =~ m|^/(S[a-zA-Z]{10})/$name$|){
+ if($sm->verify({session_id => $1, user_agent => $ci->user_agent()})){
+ return add_cookie($pl->{plugins}->{$name}->{action_imprementation}($request, $name, $1, $2), $1);
+ }
+ if ($ci->is_webkit() && $cf->webkit_newui()) {
+ return action_error($request, 401);
+ } else {
+ return action_redirect_root($request);
+ }
+ }
+ }
- if ($unread{$channel} > $config->web_lines) {
- $unread{$channel} = $config->web_lines;
- }
+ return action_public($request, $uri) || action_error($request, 404);
}
################################################################
-sub now{
- my ($sec,$min,$hour) = localtime(time);
- sprintf('%02d:%02d', $hour, $min);
+# adds session id cookie to http response object
+sub add_cookie{
+ my $response = shift;
+ my $session_id = shift;
+
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time + $cf->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);
+ my $content = sprintf("sid=%s; expires=%s; path=%s; \n", $session_id, $expiration, $cf->web_root());
+ $response->push_header('Set-Cookie', $content);
+ $response;
}
################################################################
-sub escape{
- local($_) = shift;
- s/&/&/;
- s/>/>/;
- s/</</;
- $_;
+# 通常ログインのPOST先
+# パスワードをチェックして
+# 間違っていたら / へリンクして終わり
+# 合っていたらセッションを発行し /{SESSION}/index へ
+sub action_login{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ my $content = $request->decoded_content();
+ my ($password) = ($content =~ /^password=(.*)/);
+
+ $log->log_debug("password [$password]");
+ $log->log_debug("web_password [" . $cf->web_password() . "]");
+
+ if($cf->web_password() eq $password){
+ my $s = $sm->add($ci->user_agent(), $ci->serial_key());
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ if ($ci->is_webkit() && $cf->webkit_newui()) {
+ return add_cookie($view->redirect('/'), $s->{id});
+ } else {
+ return $view->redirect("/$s->{id}/index");
+ }
+ }
+
+ # password mismatch
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->redirect('/');
}
################################################################
-sub label{
- my $accesskey = shift;
-
- if($accesskey < 10){
- sprintf('%d ', $accesskey);
- }else{
- ' ';
- }
+sub action_error {
+ my $request = shift;
+ my $error_code = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('error.html', { action => $request->uri(),
+ _http_status_code => $error_code,
+ _http_status_message => status_message($error_code) });
}
################################################################
-sub index_page{
- my $buf;
- my $accesskey = 1;
-
- for my $channel (sort {
- $mtime{$b} <=> $mtime{$a};
- }(keys(%channel_name))){
-
- $buf .= &label($accesskey);
-
- if($accesskey < 10){
- $buf .= sprintf('<a accesskey="%1d" href="%s%s">%s</a>',
- $accesskey,
- $docroot,
- uri_escape($channel),
- &compact_channel_name($channel));
- }else{
- $buf .= sprintf('<a href="%s%s">%s</a>',
- $docroot,
- uri_escape($channel),
- &compact_channel_name($channel));
- }
-
- $accesskey++;
+sub action_public {
+ my $request = shift;
+ my $uri = shift; # such as '/favicon.ico'
+ my $ci = new Keitairc::ClientInfo($request);
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->public($request, $uri);
+}
- # \e$BL$FI9T?t\e(B
- if($unread{$channel} > 0){
- $buf .= sprintf(' <a href="%s%s.update">%d</a>',
- $docroot,
- uri_escape($channel),
- $unread{$channel});
+################################################################
+# かんたんログインのPOST先
+# DoCoMoだったらiccが来ているはずなので, icc + user_agent でチェック。
+# 合っていたらセッション復帰して /{SESSION}/index へ
+sub action_login_icc{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ if($ci->is_docomo()){
+ my $docomo_foma_icc = $ci->docomo_foma_icc();
+ if(length $docomo_foma_icc){
+ if(my $s = $sm->verify({serial_key => $docomo_foma_icc,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$s->{id}/index from docomo_foma_icc");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ if($docomo_foma_icc eq $cf->docomo_foma_icc()){
+ my $s = $sm->add($ci->user_agent(), $docomo_foma_icc);
+ $log->log_debug("redirect to /$s->{id}/index from docomo_foma_icc");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('login_icc.html', { icc => $docomo_foma_icc });
+ }
}
- $buf .= '<br>';
- }
- $buf .= qq(0 <a href="$docroot" accesskey="0">refresh list</a><br>);
- $buf .= qq( - keitairc $version);
- $buf;
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('root.html', {
+ docomo_foma_icc => $cf->docomo_foma_icc(),
+ docomo_imodeid => $cf->docomo_imodeid(),
+ });
}
################################################################
-# \e$B%A%c%M%kL>>N$rC;$+$/$9$k\e(B
-sub compact_channel_name{
- local($_) = shift;
-
- # #name:*.jp \e$B$r\e(B %name \e$B$K\e(B
- if(s/:\*\.jp$//){
- s/^#/%/;
- }
-
- # \e$BKvHx$NC1FH$N\e(B @ \e$B$O<h$k\e(B (for multicast.plm)
- s/\@$//;
+# かんたんログインのPOST先
+# DoCoMoだったらiモードIDが来ているはずなので, iモードID + user_agent でチェック。
+# 合っていたらセッション復帰して /{SESSION}/index へ
+sub action_login_imodeid{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ if($ci->is_docomo()){
+ my $docomo_imodeid = $ci->docomo_imodeid();
+ if(length $docomo_imodeid){
+ if(my $s = $sm->verify({serial_key => $docomo_imodeid,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$s->{id}/index from docomo_imodeid");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ if($docomo_imodeid eq $cf->docomo_imodeid()){
+ my $s = $sm->add($ci->user_agent(), $docomo_imodeid);
+ $log->log_debug("redirect to /$s->{id}/index from docomo_imodeid");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('login_imodeid.html', { imodeid => $docomo_imodeid });
+ }
+ }
- $_;
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('root.html', {
+ docomo_foma_icc => $cf->docomo_foma_icc(),
+ docomo_imodeid => $cf->docomo_imodeid(),
+ });
}
################################################################
-sub render{
- local($_);
- my @buf;
-
- my @src = (reverse(split("\n", shift)))[0 .. $config->web_lines];
-
- for (@src){
- next unless defined;
- next unless length;
+sub action_root{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+
+ if($ci->cookie_available()){
+ my $session_id = $ci->{cookie}->{sid};
+ if(defined($session_id) && length($session_id)){
+ if($sm->verify({session_id => $session_id,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$session_id/index from cookie");
+ my $view = new Keitairc::View($cf, $ci, $session_id);
+ if ($ci->is_webkit() && $cf->webkit_newui()) {
+ return add_cookie($view->render('root_home.html', {sid => $session_id}), $session_id);
+ } else {
+ return $view->redirect("/$session_id/index");
+ }
+ }
+ }
+ }
- $_ = &escape($_);
+ if($ci->is_ezweb()){
+ my $subscriber_id = $ci->au_subscriber_id();
+ if(length $subscriber_id){
+ if(my $s = $sm->verify({serial_key => $subscriber_id,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$s->{id}/index from subscriber_id");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ if($subscriber_id eq $cf->au_subscriber_id()){
+ my $s = $sm->add($ci->user_agent(), $subscriber_id);
+ $log->log_debug("redirect to /$s->{id}/index from au_subscriber_id");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+ }
+ }
- unless(s,(http://[!-;=-\177]+),<a href="$1">$1</a>,g){
- unless(s|(www\.[!-\177]+)|<A HREF="http://$1">$1</A>|g){
- # phone to
- unless(s|(0\d{1,3}[-(]?\d{2,4}[-)]?\d{4})|<a href="tel:$1">$1</a>|g){
- s|(\w[\w.+=-]+\@[\w.-]+[\w])|<a href="mailto:$1">$1</a>|g;
+ if($ci->is_softbank()){
+ my $serial_key = $ci->softbank_serial();
+ if(length $serial_key){
+ if(my $s = $sm->verify({serial_key => $serial_key,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$s->{id}/index from softbank serial_key");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+ if($serial_key eq $cf->softbank_serial_key()){
+ my $s = $sm->add($ci->user_agent(), $serial_key);
+ $log->log_debug("redirect to /$s->{id}/index from softbank_serial_key");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
}
- }
}
- s/\s+$//;
- s/\s+/ /g;
- push @buf, $_;
- }
+ if($ci->is_emobile()){
+ my $userid = $ci->emobile_userid();
+ if(length $userid){
+ if(my $s = $sm->verify({serial_key => $userid,
+ user_agent => $ci->user_agent()})){
+ $log->log_debug("redirect to /$s->{id}/index from userid");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+
+ if($userid eq $cf->emobile_userid()){
+ my $s = $sm->add($ci->user_agent(), $userid);
+ $log->log_debug("redirect to /$s->{id}/index from emobile_userid");
+ my $view = new Keitairc::View($cf, $ci, $s->{id});
+ return $view->redirect("/$s->{id}/index");
+ }
+ }
+ }
- '<pre>' . join("\n", @buf) . '</pre>';
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->render('root.html', {
+ docomo_foma_icc => $cf->docomo_foma_icc(),
+ docomo_imodeid => $cf->docomo_imodeid(),
+ });
}
################################################################
-sub on_web_request{
- my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0];
-
- # Filter::HTTPD sometimes generates HTTP::Response objects.
- # 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')){
- $heap->{client}->put($request);
- $kernel->yield('shutdown');
- return;
- }
-
- 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;
- }
- }
+sub action_redirect_root{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ my $view = new Keitairc::View($cf, $ci);
+ return $view->redirect('/');
+}
- my $uri = $request->uri;
- my $content = '<html><head>';
+################################################################
+sub parse_message{
+ my $request = shift;
+ my $ci = new Keitairc::ClientInfo($request);
+ my $timestamp;
- # POST \e$B$5$l$F$-$?$b$N$OH/8@\e(B
- if($request->method =~ /POST/i){
- my $message = $request->content;
- $message =~ s/^m=//;
- $message =~ s/\+/ /g;
- $message = uri_unescape($message);
+ my $message = $request->content();
if(length($message)){
- $uri =~ s|^/||;
- my $channel = uri_unescape($uri);
- $poe_kernel->post('keitairc',
- 'privmsg',
- Jcode->new($channel)->jis,
- Jcode->new($message)->jis);
- &add_message($channel, $config->irc_nick,
- Jcode->new($message)->euc);
-
- # set flag for "send message"
- $send_chk = 1;
+ ($message, $timestamp) = split(/&/, $message);
+
+ $timestamp =~ s/^stamp=//g;
+
+ $message =~ s/^m=//;
+ $message =~ s/\+/ /g;
+ $message = uri_unescape($message);
+
+ if($ci->is_webkit() && !$cf->webkit_newui()){
+ $message = fix_webkit_escape($message);
+ }
}
- }
-
- if($uri eq '/'){
- $content .= '<title>' . $config->web_title . '</title>';
- $content .= '</head>';
- $content .= '<body>';
- $content .= &index_page;
- }else{
- $uri =~ s|^/||;
-
- $update_chk = ($uri =~ /.*.update/);
- if ($update_chk eq 1) {
- $uri =~ s/.update//;
+ if ($cf->webkit_newui()) {
+ # ajax で投げ込んでるので utf8 できます
+ $message = Encode::decode('utf8', $message);
+ } else {
+ $message = Encode::decode($cf->web_charset(), $message);
}
+ return ($message, $timestamp);
+}
- my $channel = uri_unescape($uri);
-
- $content .= '<title>' . $config->web_title . ": $channel</title>";
- $content .= '<body>';
-
- $content .= '<a name="1"></a>';
- $content .= '<a accesskey="7" href="#1"></a>';
- $content .= '<a accesskey="8" href="../"></a>';
-
- $content .= sprintf('<form action="%s%s" method="post">',
- $docroot, $uri);
- $content .= '<input type="text" name="m" size="10">';
- $content .= '<input type="submit" accesskey="1" value="OK[1]">';
- $content .= '<a href="../">back[8]</a><br>';
- # $content .= '<input type="submit" accesskey="1" value="聆">';
- $content .= '</form>';
-
- if(defined($channel_name{$channel})){
- if(defined($channel_buffer{$channel}) &&
- length($channel_buffer{$channel})){
- $content .= qq(<a accesskey="8" href="$docroot"></a>);
- $content .= '<a accesskey="9" href="#2"></a>';
- if (($update_chk eq 1)||($send_chk eq 1)) {
- $content .= &render($channel_recent{$channel});
- $content .= sprintf('<a accesskey="5" href="%s%s">
- ..more[5]</a>', "../", uri_escape($channel));
- } else {
- $content .= &render($channel_buffer{$channel});
+sub send_message{
+ my $request = shift;
+ my $channel = shift;
+
+ my ($message, $timestamp) = parse_message($request);
+
+ if(length($message) && length($channel)){
+ if($ib->update_timestamp($timestamp)){
+ my $enc_message = Encode::encode($cf->irc_charset(), $message);
+ my $enc_channel = Encode::encode($cf->irc_charset(), $channel);
+ $irc->yield(privmsg => $enc_channel => $enc_message);
+ my $cid = $ib->name2cid($channel);
+ $ib->add_message($cid, $message, $cf->irc_nick());
}
- $content .= '<a name="2"></a>';
- }else{
- $content .= 'no message here yet';
- }
- }else{
- $content .= "no such channel";
}
+}
- # clear check flags
- $send_chk = 0;
-
- # clear unread counter
- $unread{$channel} = 0;
+sub send_command{
+ my $request = shift;
- # clear recent messages buffer
- $channel_recent{$channel} = '';
- }
+ my ($message, $timestamp) = parse_message($request);
- $content .= '</body></html>';
+ if(length($message)){
+ if($message =~ s|^/||) {
+ my ($params, $trailing) = split(/ :/, $message, 2);
+ my @postcmd = split(/ /, $params);
+ push @postcmd, $trailing if defined $trailing;
+ # This parser may be incomplete.
+ if($postcmd[0] =~ /join/i) {
+ if($postcmd[1] =~ /^\w/) {
+ $ib->join($postcmd[1]);
+ return;
+ }
+ } elsif($postcmd[0] =~ /part/i) {
+ if($postcmd[1] =~ /^\w/) {
+ $ib->part($ib->name2cid($postcmd[1]));
+ return;
+ }
+ }
+ $irc->yield(map { Encode::encode($cf->irc_charset(), $_) } @postcmd);
+ }
+ }
+}
- my $response = HTTP::Response->new(200);
- $response->push_header('Content-type', 'text/html; charset=Shift_JIS');
- $response->content(Jcode->new($content)->sjis);
- $heap->{client}->put($response);
- $kernel->yield('shutdown');
+################################################################
+# posted string from Webkit browser
+# contains escaped utf-8 in the form %uXXXX
+# and may contains escaped Shift-JIS (web_charset) in the form \xXX
+# when operated from Safari/Mac OS X
+sub fix_webkit_escape{
+ # charset: $cf->irc_charset()
+ my $in = shift;
+ $in =~ s/\\x([0-9A-F]{2})/pack('C',hex($1))/egi;
+ #my $pi = Encode::decode('utf8', $in);
+ $in =~ s/%u([0-9A-F]{4})/pack('U',hex($1))/egi;
+ return $in;
}
__END__