4 $max_room_number = 3; //最大ルーム数
\r
5 $spilt_size = 1024 * 512; //分割するサイズ
\r
6 $reset_password_diff = 1000 * 60 * 60;
\r
7 $block_message = "メッセージの送信に失敗しました"; //ブロック時のメッセージ
\r
8 $not_match_password = "パスワードが一致しませんでした"; //パスワードが一致しない場合に表示されるメッセージ
\r
9 $password_setted_message = "パスワードを設定しました"; //パスワードが設定されたときに表示されるメッセージ
\r
10 $password_resetted_message = "パスワードをリセットしました"; //パスワードが再設定されたときに表示されるメッセージ
\r
11 $failed_set_password_message = "パスワードの設定に失敗しました"; //パスワードが再設定されたときに表示されるメッセージ
\r
12 $free_password1 = "最初に入室する人が自由にパスワードを設定できます"; //自由パスワードメッセージ1
\r
13 $free_password2 = "この部屋は使用されています。パスワードを入力してください"; //自由パスワードメッセージ2
\r
14 $fixed_password = "この部屋にはパスワードが設定されています"; //固定パスワードルーム
\r
15 $ip_ban_list_file_name = "ipbanlist.txt"; //アクセスを禁止するIPが記録されているファイル
\r
16 $port = process.env.port || 3000; //ポート
\r
17 $username = "admin"; //管理者用のページにアクセスできるユーザ名
\r
18 $password = "admin"; //管理者用のページにアクセスするのに必要なパスワード
\r
19 $token_length = 32; //トークンの長さ
\r
20 $redisHost = "localhost"; //redisサーバのアドレス
\r
21 $redisPort = 6379; //redisサーバのポート
\r
22 $redisPassword = ""; //redisサーバのパスワード
\r
23 $system_name = "system"; //システム発言を表す名前
\r
24 $log_directory = "log"; //ログファイルを置くフォルダー
\r
25 $log_file_name = "logfile%d.txt"; //ログファイル名(%dはそのままにしておくこと)
\r
26 $splited_log_file_name = "logfile%d_%s.txt" //分割後のファイル名(%dと%sはそのままにしておくこと)
\r
27 $pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //過去ログと判定する正規表現
\r
28 $logfile_pattern = "logfile[0-9]+(_*.*)?\.txt" //過去ログと判定する正規表現
\r
29 //パスワードを自由に設定できる部屋のリスト
\r
31 // password パスワードを設定する。nullにすると利用者がパスワードの設定をすることができる
\r
33 // new RoomInfomationCollection(
\r
34 // {rno:"1",password:"test"},
\r
35 // {rno:"2",password:null}
\r
37 $rooms = new RoomInfomationCollection(
\r
38 {rno:"1",password:"test"},
\r
39 {rno:"2",password:null}
\r
43 * Module dependencies.
\r
47 var express = require('express');
\r
49 var app = module.exports = express.createServer();
\r
51 var util = require("util");
\r
53 var lazy = require("lazy");
\r
55 var fs = require("fs");
\r
57 var parseCookie = require("connect").utils.parseCookie;
\r
59 var RedisStore = require("connect-redis")(express);
\r
60 var sessionStore = new RedisStore({host:$redisHost,port:$redisPort,pass:$redisPassword});
\r
62 var async = require("async");
\r
64 var path = require("path");
\r
68 app.configure(function(){
\r
69 app.disabled("view cache");
\r
70 app.set("view options", { layout: false })
\r
71 app.set("views", __dirname + "/public");
\r
72 app.set("view engine", "ejs");
\r
73 app.use(express.bodyParser());
\r
74 app.use(express.methodOverride());
\r
75 app.use(express.cookieParser());
\r
76 app.use(express.session({
\r
78 secret: "5514EA2B-C9B2-4D65-8D81-1F33A180A0C2",
\r
79 cookie: { httpOnly: false }
\r
81 app.use(app.router);
\r
82 app.use(express.static(__dirname + "/public"));
\r
85 app.configure('development', function(){
\r
86 app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
\r
89 app.configure('production', function(){
\r
90 app.use(express.errorHandler());
\r
94 app.get("/chat", function(req, res){
\r
95 var auth_string = getRandomString($token_length);
\r
96 req.session.items = {token:auth_string};
\r
98 var room_number = 0;
\r
99 if(typeof(req.query.rno) != "undefined")
\r
100 room_number = req.query.rno;
\r
102 if($rooms.IsFixedPassword(room_number))
\r
103 msg = $fixed_password;
\r
104 else if($rooms.IsContains(room_number))
\r
105 msg = $free_password2;
\r
106 if($rooms.IsFirstAuth(room_number))
\r
107 msg = $free_password1;
\r
108 res.render("chat",{rno:room_number,token:auth_string,message:msg});
\r
111 app.all("/log/" + $logfile_pattern,express.basicAuth(function (user, pass) {
\r
112 return user === $username && pass === $password;
\r
115 app.get("/log/" + $logfile_pattern, function(req, res){
\r
116 res.sendfile(__dirname + req.url);
\r
119 app.all("/admin",express.basicAuth(function (user, pass) {
\r
120 return user === $username && pass === $password;
\r
123 app.get("/admin", function(req, res){
\r
124 renderAdmin(req,res);
\r
127 app.post("/admin",function(req,res){
\r
128 if(req.session.items.token != req.body.token)
\r
130 res.send($invaild_token_message);
\r
133 if(typeof(req.body.erase) != "undefined")
\r
135 removeLog(req.body.file,function(){
\r
136 renderAdmin(req,res);
\r
139 if(typeof(req.body.registor) != "undefined")
\r
141 updateIpBanList(req.body.newbanlist,function(){
\r
142 renderAdmin(req,res);
\r
145 if(typeof(req.body.updateroom) != "undefined")
\r
147 CreateRoomsFromString(req.body.newroomlist);
\r
148 renderAdmin(req,res);
\r
152 function renderAdmin(req,res)
\r
154 var auth_string = getRandomString($token_length);
\r
155 req.session.items = {token:auth_string};
\r
157 fs.readdir($log_directory,function(err,list){
\r
158 res.render("admin", {
\r
160 log_directory:$log_directory,
\r
161 ipbanlist:getTextFromIpBanlist(ipbanlist),
\r
163 roomlist:$rooms.GetString()
\r
168 function getRandomString(length)
\r
170 var RandomString = "";
\r
171 var BaseString ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()=~|-^\@[;:],./\`{+*}<>?_";
\r
172 for(var i=0; i<length; i++) {
\r
173 RandomString += BaseString.charAt( Math.floor( Math.random() * BaseString.length));
\r
175 return RandomString
\r
178 function removeLog(files,callback)
\r
181 function(item,callback){
\r
182 fs.unlink($log_directory + "/" + item,callback);
\r
184 function(err,results){
\r
185 if(typeof(callback) == "function")
\r
191 console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
\r
197 var io = require("socket.io").listen(app);
\r
198 io.configure('production', function(){
\r
199 io.enable('browser client minification'); // minified されたクライアントファイルを送信する
\r
200 io.enable('browser client etag'); // バージョンによって etag によるキャッシングを有効にする
\r
201 io.set('log level', 1); // ログレベルを設定(デフォルトより下げている)
\r
203 var clients = new Array();
\r
205 var ipbanlist = {};
\r
207 createLogDirectory();
\r
211 for(var i = 0; i < $max_room_number; i++)
\r
214 .of(GetNameFromRoomNumber(i))
\r
215 .authorization(ParseAuthorization)
\r
216 .on("connection", function (socket) {
\r
217 console.log("connected from %s",GetClientIPAdress(socket));
\r
218 socket.json.emit("send roomlist",$rooms.GetKeys());
\r
219 socket.on("get pastLogList", function (msg) {
\r
220 ParseGetPastLogList(socket,msg);
\r
222 socket.on("get pastLog", function (msg) {
\r
223 ParseGetPastLog(socket,msg);
\r
225 socket.on("join",function(msg){
\r
226 ParseJoin(socket,msg);
\r
228 socket.on("quit",function(msg){
\r
229 ParseQuit(socket,msg);
\r
231 socket.on("set password",function(msg){
\r
232 ParseSetPassword(socket,msg);
\r
234 socket.on("send msg", function (msg) {
\r
235 ParseSendMsg(socket,msg);
\r
237 socket.on("disconnect", function (msg) {
\r
238 ParseDisconnect(socket,msg);
\r
243 function createLogDirectory()
\r
245 path.exists($log_directory,function(exists){
\r
246 if(exists == false)
\r
247 fs.mkdirSync($log_directory);
\r
251 function ParseAuthorization(handshakeData, callback)
\r
253 if(handshakeData.headers.cookie) {
\r
254 var cookie = handshakeData.headers.cookie;
\r
255 var sessionID = parseCookie(cookie)["connect.sid"];
\r
256 sessionStore.get(sessionID, function (err, session) {
\r
258 if (err || ipbanlist[handshakeData.address.address] == "r")
\r
259 result = "failed get from session store";
\r
260 else if(handshakeData.query.token != session.items.token)
\r
261 result = "invaild token";
\r
262 sessionStore.destroy(sessionID);
\r
263 callback(result,result == null && !err);
\r
266 return callback("failed get cookie", false);
\r
270 function ParseDisconnect(socket,msg)
\r
272 console.log("disconnected");
\r
275 function ParseSetPassword(socket,msg)
\r
277 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
282 if($rooms.SetPassword(rno,msg.owner,msg.password))
\r
283 newMeg.message = $password_setted_message;
\r
285 newMeg.message = $failed_set_password_message;
\r
286 ParseSendMsg(socket,newMeg);
\r
289 function ParseJoin(socket,msg)
\r
291 var ip = GetClientIPAdress(socket);
\r
293 if(ip in ipbanlist)
\r
295 socket.emit("error",$block_message);
\r
299 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
300 if($rooms.IsContains(rno))
\r
302 if($rooms.IsTimeout(rno) ||
\r
303 $rooms.IsFirstAuth(rno))
\r
305 $rooms.Reset(rno,msg.name);
\r
306 ParseGetPastLog(socket,util.format($log_file_name,rno));
\r
308 else if($rooms.Auth(rno,msg.name,msg.password))
\r
310 ParseGetPastLog(socket,util.format($log_file_name,rno));
\r
314 socket.emit("error",$not_match_password);
\r
321 message:util.format("/enteredby %s %s %s",msg.name,msg.color,msg.mailto),
\r
323 ParseSendMsg(socket,newMeg);
\r
326 function ParseQuit(socket,msg)
\r
328 var ip = GetClientIPAdress(socket);
\r
330 if(ip in ipbanlist)
\r
332 socket.emit("error",$block_message);
\r
336 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
340 message:$password_resetted_message,
\r
342 if($rooms.IsContains(rno))
\r
344 if($rooms.IsOwner(rno,msg.name))
\r
346 $rooms.Reset(rno,null);
\r
347 ParseSendMsg(socket,newMeg);
\r
349 if(!$rooms.IsFirstAuth(rno) &&
\r
350 !$rooms.IsAuthed(rno,msg.name))
\r
353 $rooms.RemoveAuth(rno,msg.name);
\r
356 newMeg.message = util.format("/quitedby %s",msg.name);
\r
357 ParseSendMsg(socket,newMeg);
\r
362 function ParseSendMsg(socket,msg)
\r
364 var ip = GetClientIPAdress(socket);
\r
366 if(ip in ipbanlist)
\r
368 socket.emit("error",$block_message);
\r
372 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
374 if(msg.name != $system_name &&
\r
375 $rooms.IsContains(rno) &&
\r
376 !$rooms.IsAuthed(rno,msg.name) &&
\r
377 !$rooms.IsOwner(rno,msg.name))
\r
382 var date = new Date();
\r
384 var repacked_msg = CreateMessage(msg.name,date,msg.message);
\r
386 socket.json.emit("req msg", repacked_msg);
\r
388 socket.json.broadcast.emit("req msg", repacked_msg);
\r
390 var path = $log_directory + "/" + util.format($log_file_name,rno);
\r
391 var log = new ChatLog(path);
\r
392 log.Save(repacked_msg,ip,rno);
\r
395 function GetNameFromRoomNumber(number)
\r
397 return "/" + number;
\r
400 function GetRoomNumberFromName(name)
\r
402 if(name.charAt(0) == "/")
\r
403 return parseInt(name.substr(1));
\r
404 throw "GetRoomNumberFromName error";
\r
407 function ParseGetPastLogList(socket,msg)
\r
409 var list = fs.readdir($log_directory,function(err,files){
\r
411 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
412 var pattern = $pastlogfile_pattern.replace("%d",rno);
\r
413 for(var i = 0; i < files.length; i++)
\r
415 var logname = files[i];
\r
416 if(logname.match(pattern))
\r
417 text += files[i] + "\n";
\r
419 socket.emit("req pastloglist",text);
\r
423 function ParseGetPastLog(socket,file)
\r
427 var path = $log_directory + "/" + file;
\r
428 var log = new ChatLog(path);
\r
429 log.ToArray(function(array){
\r
430 socket.json.emit("req pastlog",array);
\r
434 function ChatLog(path)
\r
436 this.ToArray = function(callback)
\r
438 var state = fs.stat(path,function(err,state){
\r
441 var array = new Array();
\r
442 var stream = fs.createReadStream(path);
\r
445 .forEach(function(line){
\r
446 array.push(CreateMessageFromText(line.toString()));
\r
454 this.Save = function(msg,ip,rno){
\r
455 var text = GetTextFromMessage(msg,ip);
\r
457 SplitLog(rno,function(){
\r
458 WritePastLog(path,text);
\r
462 function GetTextFromMessage(msg,ip)
\r
464 var text = msg.name + "<>" +
\r
472 function SplitLog(rno,callback)
\r
474 var state = fs.stat(path,function(err,state){
\r
475 if(err && typeof(callback) == "function")
\r
480 if(state.size > $spilt_size)
\r
482 var date = new Date();
\r
483 var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds();
\r
485 var newpath = $log_directory + "/" +
\r
486 util.format($splited_log_file_name,rno,dateString);
\r
487 fs.rename(path,newpath,callback);
\r
489 if(typeof(callback) == "function")
\r
495 function WritePastLog(path,text)
\r
498 function(callback){
\r
499 fs.open(path,"a",callback);
\r
501 function(fd,callback){
\r
502 var buf = new Buffer(text);
\r
503 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){
\r
514 function GetClientIPAdress(socket)
\r
516 return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;
\r
520 function CreateMessage(name,date,message)
\r
522 var result = {name:name,
\r
527 function CreateMessageFromText(text)
\r
529 var data = text.split("<>");
\r
530 var msg = {name:data[0],
\r
536 //RoomInfomationCollecionクラス
\r
537 function RoomInfomationCollection()
\r
539 var collection = {};
\r
540 this.Clear = function(){
\r
543 this.Add = function(rno,pass){
\r
544 collection[rno] = {time : null,
\r
549 collection[rno].owner = $system_name;
\r
551 this.Reset = function(rno,owner){
\r
552 var date = new Date();
\r
553 var time = date.getTime();
\r
554 collection[rno].password = null;
\r
555 collection[rno].authed_list = {};
\r
556 collection[rno].owner = owner;
\r
557 collection[rno].time = time;
\r
558 console.log(util.format("password is reseted in %s",rno));
\r
560 this.IsContains = function(rno){
\r
561 return rno in collection;
\r
563 this.IsFirstAuth = function(rno){
\r
564 if(!this.IsContains(rno) || typeof(collection[rno].owner) == "undefined")
\r
566 return collection[rno].owner == null;
\r
568 this.IsAuthed = function(rno,name){
\r
569 if(!this.IsContains(rno))
\r
571 console.log(util.inspect(collection[rno].authed_list));
\r
572 return name == collection[rno].owner ||
\r
573 name in collection[rno].authed_list;
\r
575 this.IsFixedPassword = function(rno){
\r
576 if(!this.IsContains(rno))
\r
578 return collection[rno].owner == $system_name;
\r
580 this.IsOwner = function(rno,name){
\r
581 return this.IsContains(rno) &&
\r
582 typeof(collection[rno].owner) != "undefined" &&
\r
583 collection[rno].owner == name;
\r
585 this.IsTimeout = function(rno){
\r
586 var date = new Date();
\r
587 var time = date.getTime();
\r
588 return this.IsContains(rno) &&
\r
589 !this.IsFixedPassword(rno) &&
\r
590 (typeof(collection[rno].time) != "undefined" &&
\r
591 time - collection[rno].time >= $reset_password_diff);
\r
593 this.RemoveAuth = function(rno,name)
\r
595 delete collection[rno].authed_list[name];
\r
597 this.Auth = function(rno,name,password){
\r
598 if(typeof(collection[rno].password) != "undefined" &&
\r
599 collection[rno].password != password)
\r
601 var date = new Date();
\r
602 var time = date.getTime();
\r
603 collection[rno].time = time;
\r
604 collection[rno].authed_list[name] = "";
\r
607 this.SetPassword = function(rno,owner,password){
\r
608 if(this.IsContains(rno) &&
\r
609 owner == collection[rno].owner &&
\r
610 !this.IsFixedPassword(rno))
\r
612 var date = new Date();
\r
613 collection[rno].time = date.getTime();
\r
614 collection[rno].password = password;
\r
616 console.log(util.format("password is seted to %s in %s",password,rno));
\r
621 this.GetString = function(){
\r
623 for(var rno in collection)
\r
625 var pass = collection[rno].password;
\r
628 retval += rno + ":" + pass + "\r\n";
\r
632 this.GetKeys = function(){
\r
634 for(var rno in collection)
\r
641 for(var i=0; i<arguments.length; i++)
\r
643 this.Add(arguments[i].rno,arguments[i].password);
\r
647 function CreateRoomsFromString(str)
\r
650 var lines = str.split("\r\n");
\r
651 for(var i in lines)
\r
653 var token = lines[i].split(":");
\r
654 console.log(util.inspect(token));
\r
655 if(token.length == 1)
\r
657 $rooms.Add(token[0],null);
\r
659 else if(token.length == 2)
\r
661 var rno = token[0];
\r
662 var pass = token[1];
\r
665 $rooms.Add(rno, pass);
\r
671 function getTextFromIpBanlist(list)
\r
674 for(var key in ipbanlist)
\r
676 if(ipbanlist[key] == "")
\r
677 text += key + "\r\n";
\r
679 text += key + ":" + ipbanlist[key] + "\r\n";
\r
684 function updateIpBanList(text,callfunc)
\r
687 function(callback){
\r
688 fs.open($ip_ban_list_file_name,"w",callback);
\r
690 function(fd,callback){
\r
691 var buf = new Buffer(text);
\r
692 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){
\r
696 function(fd,callback){
\r
697 fs.close(fd,function(){
\r
698 getIpBanList(callfunc);
\r
704 function getIpBanList(callback)
\r
707 path.exists($ip_ban_list_file_name,function(exists){
\r
708 if(exists == false)
\r
710 if(typeof(callback) == "function")
\r
714 var stream = fs.createReadStream($ip_ban_list_file_name);
\r
717 .forEach(function(line){
\r
718 var token = line.toString().split(":");
\r
719 var ip = token[0].replace(/(\r|\n|\r\n)/gm, "");
\r
720 if(token.length == 1)
\r
721 ipbanlist[ip] = "";
\r
723 ipbanlist[ip] = token[1];
\r
726 if(typeof(callback) == "function")
\r