OSDN Git Service

First cut of emobile support. Closes #12453.
[keitairc/keitairc.git] / keitairc
1 #!/usr/bin/perl
2 # keitairc
3 # $Id: keitairc,v 1.55 2008-05-13 02:53:31 matusita Exp $
4 # $Source: /home/ishikawa/work/keitairc/tmp/keitairc/keitairc,v $
5 #
6 # Copyright (c) 2003-2008 Jun Morimoto <morimoto@mrmt.net>
7 # This program is covered by the GNU General Public License 2
8 #
9 # Depends: libpoe-component-irc-perl,
10 #   liburi-perl, libwww-perl, libappconfig-perl, libproc-daemon-perl,
11 #   libhtml-template-perl
12 #
13 # 00location_receiver plugin use XML::Simple, so if you want to use it
14 #    Depends: libxml-simple-perl
15
16 use lib qw(lib /usr/share/keitairc/lib);
17 use strict;
18 use Encode;
19 use POE;
20 use POE::Filter::HTTPD;
21 use POE::Component::IRC;
22 use POE::Component::Server::TCP;
23 use URI::Escape;
24 use HTML::Template;
25 use HTTP::Response;
26 use Proc::Daemon;
27 use Keitairc::Config;
28 use Keitairc::View;
29 use Keitairc::IrcBuffer;
30 use Keitairc::IrcCallback;
31 use Keitairc::ClientInfo;
32 use Keitairc::SessionManager;
33 use Keitairc::Plugins;
34
35 our $cf = new Keitairc::Config('2.0b5', @ARGV);
36 our $ib = new Keitairc::IrcBuffer({history => $cf->web_lines()});
37 our $sm = new Keitairc::SessionManager({default_ttl => $cf->session_ttl()});
38 our $pl = new Keitairc::Plugins({config => $cf});
39
40 # daemonize
41 if($cf->daemonize()){
42         Proc::Daemon::Init;
43         if(length $cf->pid_dir()){
44                 if (open(PID, '> ' . $cf->pid_dir() . '/' . $cf->pid_file())) {
45                         print PID $$, "\n";
46                         close(PID);
47                 }
48         }
49 }
50
51 # create irc component
52 our $irc = POE::Component::IRC->spawn(
53         Alias => 'keitairc_irc',
54         Nick => $cf->irc_nick(),
55         Username => $cf->irc_username(),
56         Ircname => $cf->irc_desc(),
57         Server => $cf->irc_server(),
58         Port => $cf->irc_port(),
59         Password => $cf->irc_password());
60
61 # create POE session
62 POE::Session->create(
63         heap => {
64                 seen_traffic => 0,
65                 disconnect_msg => 1,
66                 Config => $cf,
67                 Irc => $irc,
68                 IrcBuffer => $ib,
69         },
70         inline_states => {
71                 _start => \&Keitairc::IrcCallback::irc_start,
72                 autoping => \&Keitairc::IrcCallback::irc_autoping,
73                 connect => \&Keitairc::IrcCallback::irc_connect,
74                 irc_001 => \&Keitairc::IrcCallback::irc_001,
75                 irc_join => \&Keitairc::IrcCallback::irc_join,
76                 irc_part => \&Keitairc::IrcCallback::irc_part,
77                 irc_quit => \&Keitairc::IrcCallback::irc_quit,
78                 irc_public => \&Keitairc::IrcCallback::irc_public,
79                 irc_notice => \&Keitairc::IrcCallback::irc_notice,
80                 irc_mode => \&Keitairc::IrcCallback::irc_mode,
81                 irc_nick => \&Keitairc::IrcCallback::irc_nick,
82                 irc_msg => \&Keitairc::IrcCallback::irc_msg,
83                 irc_topic => \&Keitairc::IrcCallback::irc_topic,
84                 irc_332 => \&Keitairc::IrcCallback::irc_topicraw,
85                 irc_352 => \&Keitairc::IrcCallback::irc_whoreply,
86                 irc_ctcp_action => \&Keitairc::IrcCallback::irc_ctcp_action,
87                 irc_disconnected => \&Keitairc::IrcCallback::irc_reconnect,
88                 irc_error => \&Keitairc::IrcCallback::irc_reconnect,
89                 irc_socketerr => \&Keitairc::IrcCallback::irc_reconnect,
90         });
91
92 # create web server component
93 POE::Component::Server::TCP->new(
94         Alias => 'keitairc',
95         Port => $cf->web_port(),
96         ClientFilter => 'POE::Filter::HTTPD',
97         ClientInput => \&http_request);
98
99 # fire up main loop
100 $poe_kernel->run();
101 exit 0;
102
103 ################################################################
104 sub http_request{
105         my ($kernel, $heap, $request) = @_[KERNEL, HEAP, ARG0];
106
107         # Filter::HTTPD sometimes generates HTTP::Response objects.
108         # They indicate (and contain the response for) errors that occur
109         # while parsing the client's HTTP request.  It's easiest to send
110         # the responses as they are and finish up.
111         if($request->isa('HTTP::Response')){
112                 $heap->{client}->put($request);
113         }elsif(my $response = dispatch($request)){
114                 $heap->{client}->put($response);
115         }
116
117         $kernel->yield('shutdown');
118 }
119
120 ################################################################
121 sub dispatch{
122         my $request = shift;
123         my $uri = $request->uri();
124         my $ci = new Keitairc::ClientInfo($request);
125
126         ::log_debug("dispatch: $uri");
127
128         {
129                 # chop off $cf->web_root()
130                 my $root = $cf->web_root();
131                 $uri =~ s|$root|/|;
132         }
133
134         if($uri eq '/'){
135                 return action_root($request);
136         }
137
138         if($uri eq '/login'){
139                 return action_login($request);
140         }
141
142         if($uri eq '/login_icc'){
143                 return action_login_icc($request);
144         }
145
146         if($uri eq '/login_imodeid?guid=ON'){
147                 return action_login_imodeid($request);
148         }
149
150         if($uri eq '/robots.txt'){
151                 return action_robots_txt($request);
152         }
153
154         for my $name ($pl->list_action_plugins()){
155                 if($uri =~ m|^/(S[a-zA-Z]{10})/$name/(.*)| ||
156                    $uri =~ m|^/(S[a-zA-Z]{10})/$name$|){
157                         if($sm->verify({session_id => $1, user_agent => $ci->user_agent()})){
158                                 return add_cookie($pl->{plugins}->{$name}->{action_imprementation}($request, $name, $1, $2), $1);
159                         }
160                         return action_redirect_root($request);
161                 }
162         }
163
164         ::log("dispatch: don't know how to dispatch uri[$uri]");
165         return action_404($request);
166 }
167
168 ################################################################
169 # adds session id cookie to http response object
170 sub add_cookie{
171         my $response = shift;
172         my $session_id = shift;
173
174         my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time + $cf->cookie_ttl());
175         my $expiration =
176                 sprintf('%.3s, %.2d-%.3s-%.4s %.2d:%.2d:%.2d',
177                         qw(Sun Mon Tue Wed Thu Fri Sat)[$wday],
178                         $mday,
179                         qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$mon],
180                         $year + 1900,
181                         $hour,
182                         $min,
183                         $sec);
184         my $content = sprintf("sid=%s; expires=%s; \n", $session_id, $expiration);
185         $response->push_header('Set-Cookie', $content);
186         $response;
187 }
188
189 ################################################################
190\92Ê\8fí\83\8d\83O\83C\83\93\82ÌPOST\90æ
191\83p\83X\83\8f\81[\83h\82ð\83`\83F\83b\83N\82µ\82Ä
192\8aÔ\88á\82Á\82Ä\82¢\82½\82ç / \82Ö\83\8a\83\93\83N\82µ\82Ä\8fI\82í\82è
193\8d\87\82Á\82Ä\82¢\82½\82ç\83Z\83b\83V\83\87\83\93\82ð\94­\8ds\82µ /{SESSION}/index \82Ö
194 sub action_login{
195         my $request = shift;
196         my $ci = new Keitairc::ClientInfo($request);
197         my $content = $request->decoded_content();
198         my ($password) = ($content =~ /^password=(.*)/);
199
200         ::log_debug("password [$password]");
201         ::log_debug("web_password [" . $cf->web_password() . "]");
202
203         if($cf->web_password() eq $password){
204                 my $s = $sm->add($ci->{header}->{user_agent}, $ci->serial_key());
205                 my $view = new Keitairc::View($cf, $ci, $s->{id});
206                 return $view->redirect("/$s->{id}/index");
207         }
208
209         # password mismatch
210         my $view = new Keitairc::View($cf, $ci);
211         return $view->redirect('/');
212 }
213
214 ################################################################
215 sub action_404{
216         my $request = shift;
217         my $ci = new Keitairc::ClientInfo($request);
218         my $view = new Keitairc::View($cf, $ci);
219         return $view->render('404.html', { action => $request->uri() });
220 }
221
222 ################################################################
223 sub action_robots_txt{
224         my $request = shift;
225         my $ci = new Keitairc::ClientInfo($request);
226         my $view = new Keitairc::View($cf, $ci);
227         return $view->render('robots.txt', { content_type => 'text/plain' });
228 }
229
230 ################################################################
231\82©\82ñ\82½\82ñ\83\8d\83O\83C\83\93\82ÌPOST\90æ
232 # DoCoMo\82¾\82Á\82½\82çicc\82ª\97\88\82Ä\82¢\82é\82Í\82¸\82È\82Ì\82Å, icc + user_agent \82Å\83`\83F\83b\83N\81B
233\8d\87\82Á\82Ä\82¢\82½\82ç\83Z\83b\83V\83\87\83\93\95\9c\8bA\82µ\82Ä /{SESSION}/index \82Ö
234 sub action_login_icc{
235         my $request = shift;
236         my $ci = new Keitairc::ClientInfo($request);
237         if($ci->is_docomo()){
238                 my $docomo_foma_icc = $ci->docomo_foma_icc();
239                 if(length $docomo_foma_icc){
240                         if(my $s = $sm->verify({serial_key => $docomo_foma_icc,
241                                                 user_agent => $ci->user_agent()})){
242                                 ::log_debug("redirect to /$s->{id}/index from docomo_foma_icc");
243                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
244                                 return $view->redirect("/$s->{id}/index");
245                         }
246
247                         if($docomo_foma_icc eq $cf->docomo_foma_icc()){
248                                 my $s = $sm->add($ci->user_agent(), $docomo_foma_icc);
249                                 ::log_debug("redirect to /$s->{id}/index from docomo_foma_icc");
250                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
251                                 return $view->redirect("/$s->{id}/index");
252                         }
253
254                         my $view = new Keitairc::View($cf, $ci);
255                         return $view->render('login_icc.html', { icc => $docomo_foma_icc });
256                 }
257         }
258
259         my $view = new Keitairc::View($cf, $ci);
260         return $view->render('root.html', {
261                 docomo_foma_icc => $cf->docomo_foma_icc(),
262                 docomo_imodeid => $cf->docomo_imodeid(),
263                         });
264 }
265
266 ################################################################
267\82©\82ñ\82½\82ñ\83\8d\83O\83C\83\93\82ÌPOST\90æ
268 # DoCoMo\82¾\82Á\82½\82çi\83\82\81[\83hID\82ª\97\88\82Ä\82¢\82é\82Í\82¸\82È\82Ì\82Å, i\83\82\81[\83hID + user_agent \82Å\83`\83F\83b\83N\81B
269\8d\87\82Á\82Ä\82¢\82½\82ç\83Z\83b\83V\83\87\83\93\95\9c\8bA\82µ\82Ä /{SESSION}/index \82Ö
270 sub action_login_imodeid{
271         my $request = shift;
272         my $ci = new Keitairc::ClientInfo($request);
273         if($ci->is_docomo()){
274                 my $docomo_imodeid = $ci->{header}->{x_dcmguid};
275                 if(length $docomo_imodeid){
276                         if(my $s = $sm->verify({serial_key => $docomo_imodeid,
277                                                 user_agent => $ci->user_agent()})){
278                                 ::log_debug("redirect to /$s->{id}/index from docomo_imodeid");
279                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
280                                 return $view->redirect("/$s->{id}/index");
281                         }
282
283                         if($docomo_imodeid eq $cf->docomo_imodeid()){
284                                 my $s = $sm->add($ci->user_agent(), $docomo_imodeid);
285                                 ::log_debug("redirect to /$s->{id}/index from docomo_imodeid");
286                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
287                                 return $view->redirect("/$s->{id}/index");
288                         }
289
290                         my $view = new Keitairc::View($cf, $ci);
291                         return $view->render('login_imodeid.html', { imodeid => $docomo_imodeid });
292                 }
293         }
294
295         my $view = new Keitairc::View($cf, $ci);
296         return $view->render('root.html', {
297                 docomo_foma_icc => $cf->docomo_foma_icc(),
298                 docomo_imodeid => $cf->docomo_imodeid(),
299                         });
300 }
301
302 ################################################################
303 sub action_root{
304         my $request = shift;
305         my $ci = new Keitairc::ClientInfo($request);
306
307         if($ci->cookie_available()){
308                 my $session_id = $ci->{cookie}->{sid};
309                 if(length $session_id){
310                         if($sm->verify({session_id => $session_id,
311                                         user_agent => $ci->user_agent()})){
312                                 ::log_debug("redirect to /$session_id/index from cookie");
313                                 my $view = new Keitairc::View($cf, $ci, $session_id);
314                                 return $view->redirect("/$session_id/index");
315                         }
316                 }
317         }
318
319         if($ci->is_ezweb()){
320                 my $subscriber_id = $ci->{header}->{x_up_subno};
321                 if(length $subscriber_id){
322                         if(my $s = $sm->verify({serial_key => $subscriber_id,
323                                                 user_agent => $ci->user_agent()})){
324                                 ::log_debug("redirect to /$s->{id}/index from subscriber_id");
325                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
326                                 return $view->redirect("/$s->{id}/index");
327                         }
328
329                         if($subscriber_id eq $cf->au_subscriber_id()){
330                                 my $s = $sm->add($ci->user_agent(), $subscriber_id);
331                                 ::log_debug("redirect to /$s->{id}/index from au_subscriber_id");
332                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
333                                 return $view->redirect("/$s->{id}/index");
334                         }
335                 }
336         }
337
338         if($ci->is_softbank()){
339                 my $serial_key = $ci->softbank_serial();
340                 if(length $serial_key){
341                         if(my $s = $sm->verify({serial_key => $serial_key,
342                                                 user_agent => $ci->user_agent()})){
343                                 ::log_debug("redirect to /$s->{id}/index from softbank serial_key");
344                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
345                                 return $view->redirect("/$s->{id}/index");
346                         }
347                         if($serial_key eq $cf->softbank_serial_key()){
348                                 my $s = $sm->add($ci->user_agent(), $serial_key);
349                                 ::log_debug("redirect to /$s->{id}/index from softbank_serial_key");
350                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
351                                 return $view->redirect("/$s->{id}/index");
352                         }
353                 }
354         }
355
356         if($ci->is_emobile()){
357                 my $userid = $ci->{header}->{x_em_uid};
358                 if(length $userid){
359                         if(my $s = $sm->verify({serial_key => $userid,
360                                                 user_agent => $ci->user_agent()})){
361                                 ::log_debug("redirect to /$s->{id}/index from userid");
362                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
363                                 return $view->redirect("/$s->{id}/index");
364                         }
365
366                         if($userid eq $cf->emobile_userid()){
367                                 my $s = $sm->add($ci->user_agent(), $userid);
368                                 ::log_debug("redirect to /$s->{id}/index from emobile_userid");
369                                 my $view = new Keitairc::View($cf, $ci, $s->{id});
370                                 return $view->redirect("/$s->{id}/index");
371                         }
372                 }
373         }
374
375         my $view = new Keitairc::View($cf, $ci);
376         return $view->render('root.html', {
377                 docomo_foma_icc => $cf->docomo_foma_icc(),
378                 docomo_imodeid => $cf->docomo_imodeid(),
379                         });
380 }
381
382 ################################################################
383 sub action_redirect_root{
384         my $request = shift;
385         my $ci = new Keitairc::ClientInfo($request);
386         my $view = new Keitairc::View($cf, $ci);
387         return $view->redirect('/');
388 }
389
390 ################################################################
391 sub send_message{
392         my $request = shift;
393         my $channel = shift;
394
395         my $message = $request->content();
396         $message =~ s/^m=//;
397         $message =~ s/\+/ /g;
398         $message = uri_unescape($message);
399
400         if(length($message)){
401                 my $jis = $message;
402                 my $euc = $message;
403                 Encode::from_to($jis, 'shiftjis', 'jis');
404                 Encode::from_to($euc, 'shiftjis', 'euc-jp');
405                 if($jis =~ s|^/||) {
406                         my ($params, $trailing) = split(/ :/, $jis, 2);
407                         my @postcmd = split(/ /, $params);
408                         push @postcmd, $trailing if defined $trailing;
409                         # This parser may be incomplete.
410                         if($postcmd[0] =~ /join/i) {
411                                 if($postcmd[1] =~ /^\w/) {
412                                         $ib->join($postcmd[1]);
413                                         return;
414                                 }
415                         } elsif($postcmd[0] =~ /part/i) {
416                                 if($postcmd[1] =~ /^\w/) {
417                                         $ib->part($ib->name2cid($postcmd[1]));
418                                         return;
419                                 }
420                         }
421                         $irc->yield(@postcmd);
422                 } elsif(length($channel)){
423                         $irc->yield(privmsg => $channel => $jis);
424                         my $cid = $ib->name2cid($channel);
425                         $ib->add_message($cid, $euc, $cf->irc_nick());
426                         $ib->message_added(1);
427                 }
428         }
429 }
430
431 ################################################################
432\93ü\97Í\82Í euc-jp
433 sub render_line{
434         local($_);
435         my $in = shift;
436         my $session_id = shift;
437         my $reverse = shift;
438         my @message;
439         my $buf;
440
441         @message = (split("\n", $in))[0 .. $cf->web_lines()];
442         if ($reverse) {
443                 @message = reverse(@message);
444         }
445         for (@message){
446                 next unless defined;
447                 next unless length;
448
449                 $_ = $ib->simple_escape($_);
450                 $_ = $ib->colorize($_);
451
452                 for my $name ($pl->list_replace_plugins()){
453                         last if s/$pl->{plugins}->{$name}->{message_replace_regexp}/$pl->{plugins}->{$name}->{message_replace_imprementation}($session_id, $1, $2, $3, $4, $5, $6, $7, $8, $9)/eg;
454                 }
455
456                 s/\s+$//;
457                 s/\s+/ /g;
458                 $buf .= "$_<br />";
459         }
460
461         Encode::from_to($buf, 'euc-jp', 'shiftjis');
462         $buf;
463 }
464
465 ################################################################
466 sub log{
467         my $m = shift;
468         warn "keitairc: $m\n";
469 }
470
471 sub log_die{
472         my $m = shift;
473         die "keitairc: $m\n";
474 }
475
476 sub log_debug{
477         my $m = shift;
478         if($cf->debug()){
479                 warn "keitairc(debug): $m\n";
480         }
481 }
482
483 __END__