1 $spilt_size = 1024 * 512; //分割するサイズ
\r
2 $reset_password_diff = 1000 * 60 * 60; //ルームパスワードをリセットする間隔
\r
3 $gc_time_interval = 1000 * 60 * 60; //ゴミ掃除を行う間隔
\r
4 $ip_ban_list_file_name = "ipbanlist.txt"; //アクセスを禁止するIPが記録されているファイル
\r
5 $room_configure_file_name = "roomlist.txt"; //ルームの設定が記録されているファイル
\r
6 $system_name = "system"; //システム発言を表す名前
\r
7 $log_directory = "log"; //ログファイルを置くフォルダー
\r
8 $log_file_name = "logfile%d.txt"; //ログファイル名(%dはそのままにしておくこと)
\r
9 $splited_log_file_name = "logfile%d_%s.txt" //分割後のファイル名(%dと%sはそのままにしておくこと)
\r
10 $pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //過去ログと判定する正規表現
\r
12 var resource = require("./resources.js");
\r
13 var config = require("./configure.js");
\r
14 var lazy = require("./lazy.js");
\r
15 var security = require("./security.js");
\r
16 var fs = require("fs");
\r
17 var async = require("async");
\r
18 var path = require("path");
\r
19 var util = require("util");
\r
20 var cookie = require("express/node_modules/cookie");
\r
21 var connectUtils = require("express/node_modules/connect/lib/utils");
\r
23 var clients = new Array();
\r
27 module.exports = function(app,server,express,session){
\r
28 sessionStore = session;
\r
29 app.get("/chat", chat_proc);
\r
30 app.all("/log/*",express.basicAuth(auth_proc));
\r
31 app.get("/log/*",log_proc);
\r
32 app.all("/admin_chat",express.basicAuth(auth_proc));
\r
33 app.get("/admin_chat", adminchat_proc);
\r
34 app.all("/admin",express.basicAuth(auth_proc));
\r
35 app.get("/admin", admin_proc);
\r
36 app.post("/admin",admin_postproc);
\r
38 var io = require("socket.io").listen(server);
\r
39 io.configure("production", function(){
\r
40 io.enable("browser client minification"); // minified されたクライアントファイルを送信する
\r
41 io.enable("browser client etag"); // バージョンによって etag によるキャッシングを有効にする
\r
42 io.set("log level", 1); // ログレベルを設定(デフォルトより下げている)
\r
45 for(var i = 0; i < config.max_room_number; i++)
\r
48 .of(GetNameFromRoomNumber(i))
\r
49 .authorization(ParseAuthorization)
\r
50 .on("connection",ParseConnect);
\r
54 function chat_proc(req, res){
\r
55 var info = new security.SessionInfomation(false);
\r
56 req.session.items = info;
\r
58 var room_number = 0;
\r
59 if(typeof(req.query.rno) != "undefined")
\r
60 room_number = req.query.rno;
\r
61 res.render("chat",{rno:room_number,token:info.token});
\r
64 function auth_proc(user, pass) {
\r
65 return user === config.username && pass === config.password;
\r
68 function log_proc(req, res) {
\r
69 res.sendfile(__dirname + req.url);
\r
72 function adminchat_proc(req, res){
\r
73 var info = new security.SessionInfomation(true);
\r
74 req.session.items = info;
\r
76 var room_number = 0;
\r
77 if(typeof(req.query.rno) != "undefined")
\r
78 room_number = req.query.rno;
\r
79 res.render("chat",{rno:room_number,token:info.token});
\r
82 function admin_postproc(req,res){
\r
83 if(req.session.items.token != req.body.token)
\r
85 res.send(resource.invaild_token_message);
\r
88 if(typeof(req.body.erase) != "undefined")
\r
90 removeLog(req.body.file,function(){
\r
91 res.redirect("/admin");
\r
94 if(typeof(req.body.registor) != "undefined")
\r
96 ipbanlist.Update(req.body.newbanlist,function(){
\r
97 res.redirect("/admin");
\r
100 if(typeof(req.body.updateroom) != "undefined")
\r
102 $rooms.Update(req.body.newroomlist,function(){
\r
103 res.redirect("/admin");
\r
108 function admin_proc(req,res)
\r
110 var info = new security.SessionInfomation(true);
\r
111 req.session.items = info;
\r
112 var iplist = ipbanlist.GetText();
\r
114 fs.readdir($log_directory,function(err,list){
\r
115 res.render("admin", {
\r
117 log_directory:$log_directory,
\r
120 roomlist:$rooms.GetString()
\r
125 function removeLog(files,callback)
\r
127 if(typeof(files) == "undefined")
\r
129 if(typeof(callback) == "function")
\r
135 function(item,callback){
\r
136 fs.unlink($log_directory + "/" + item,callback);
\r
138 function(err,results){
\r
139 if(typeof(callback) == "function")
\r
144 //RoomInfomationCollecionクラス
\r
145 function RoomInfomationCollection()
\r
147 var MySQLPool = new require("./mysql_pool.js");
\r
148 var pool = new MySQLPool({
\r
149 host : config.db_host,
\r
150 user : config.db_user,
\r
151 password : config.db_password,
\r
152 port : config.db_port,
\r
153 database : "webchat",
\r
155 var collection = {};
\r
156 this.Get = function(rno){
\r
157 return collection[rno];
\r
159 this.IsContains = function(rno){
\r
160 return rno in collection;
\r
162 this.GetString = function(){
\r
164 for(var rno in collection)
\r
166 if($rooms.Get(rno).IsVolatile())
\r
168 var pass = collection[rno].password;
\r
171 var hiddenlog = collection[rno].hiddenlog;
\r
172 retval += rno + ":" + pass + ":" + hiddenlog + "\r\n";
\r
176 this.GetKeys = function(){
\r
178 for(var rno in collection)
\r
184 this.Update = function(text,callfunc){
\r
188 pool.query("TRUNCATE TABLE rooms",null,next);
\r
190 function(result,next){
\r
191 lines = text.split("\r\n");
\r
192 var items = new Array();
\r
193 for(var i = 0; i < lines.length; i++)
\r
197 var token = lines[i].split(":");
\r
198 if(token.length == 1)
\r
200 Add(token[0],null,false);
\r
201 items.push(new Array(token[0],null,false));
\r
203 else if(token.length == 2)
\r
205 var rno = token[0];
\r
206 var pass = token[1];
\r
209 Add(rno, pass,false);
\r
210 items.push(new Array(token[0],pass,false));
\r
212 else if(token.length == 3)
\r
214 var rno = token[0];
\r
215 var pass = token[1];
\r
218 var hiddenlog = false;
\r
219 if(token[2] == "true")
\r
221 Add(rno, pass,hiddenlog);
\r
222 items.push(new Array(token[0],pass,hiddenlog));
\r
225 pool.query("INSERT INTO rooms VALUES ?",[items],callfunc);
\r
229 function GetRoomList(callback){
\r
233 pool.query("SELECT * FROM rooms",null,next);
\r
235 function(result,next){
\r
236 for(var i = 0; i < result.length; i++)
\r
238 //MySQLではTINYINTが使われている
\r
239 Add(result[i].number,result[i].password,result[i].hiddenlog != 0);
\r
247 for(var i = 0; i < config.max_room_number; i++)
\r
250 function Add(rno,pass,hiddenlogflag){
\r
251 collection[rno] = new RoomInfomation(pass,hiddenlogflag);
\r
253 collection[rno].owner = $system_name;
\r
255 var $gc_interval_id = setInterval(function(){
\r
256 for(var rno in this.rom_list)
\r
257 collection[rno].GCRomList();
\r
258 },$gc_time_interval);
\r
262 //RoomInfomationクラス
\r
263 function RoomInfomation(pass,hiddenlogflag)
\r
265 this.password = pass;
\r
266 this.rom_list = {};
\r
267 this.authed_list = {};
\r
270 this.hiddenlog = hiddenlogflag;
\r
271 this.GetConfig = function(){
\r
272 var roomconfig = {};
\r
273 if(this.IsVolatile() == false)
\r
275 if(this.IsFixedPassword())
\r
276 roomconfig.type = 2;
\r
277 else if(this.IsHiddenLogFromRom())
\r
278 roomconfig.type = 3;
\r
280 roomconfig.type = 1;
\r
281 roomconfig.IsOwned = !this.IsFirstAuth();
\r
283 roomconfig.type = 0;
\r
287 this.IsVolatile = function(){
\r
288 return this.owner == null &&
\r
289 this.password == null &&
\r
290 this.time == null &&
\r
291 this.hiddenlog == null;
\r
293 this.GetRomCount = function(){
\r
295 for(var key in this.rom_list)
\r
299 this.AddRom = function(ip){
\r
300 var date = new Date();
\r
301 this.rom_list[ip] = {time:date.getTime()};
\r
303 this.RemoveRom = function(ip){
\r
304 delete this.rom_list[ip];
\r
306 this.Reset = function(owner){
\r
307 var date = new Date();
\r
308 var time = date.getTime();
\r
309 this.password = null;
\r
310 this.authed_list = {};
\r
311 this.owner = owner;
\r
314 this.IsFirstAuth = function(){
\r
315 return this.owner == null;
\r
317 this.IsAuthed = function(name){
\r
318 return name == this.owner ||
\r
319 name in this.authed_list;
\r
321 this.IsHiddenLogFromRom = function(){
\r
322 return this.hiddenlog;
\r
324 this.IsFixedPassword = function(){
\r
325 return this.owner == $system_name;
\r
327 this.IsOwner = function(name){
\r
328 return this.owner == name;
\r
330 this.IsTimeout = function(){
\r
331 var date = new Date();
\r
332 var current_time = date.getTime();
\r
333 return !this.IsFixedPassword() &&
\r
334 current_time - this.time >= $reset_password_diff;
\r
336 this.RemoveAuth = function(name)
\r
338 delete this.authed_list[name];
\r
340 this.Auth = function(name,password){
\r
341 if(this.password != password)
\r
343 var date = new Date();
\r
344 var time = date.getTime();
\r
346 this.authed_list[name] = "";
\r
349 this.SetPassword = function(owner,password){
\r
350 if(owner == this.owner &&
\r
351 !this.IsFixedPassword() &&
\r
352 !this.IsHiddenLogFromRom())
\r
354 var date = new Date();
\r
355 this.time = date.getTime();
\r
356 this.password = password;
\r
361 this.GCRomList = function(){
\r
362 var date = new Date();
\r
363 var current_time = date.getTime();
\r
364 for(var ip in this.rom_list)
\r
366 if(current_time - this.rom_list[ip].time >= $gc_time_interval)
\r
367 delete this.rom_list[ip];
\r
373 function IpBanCollecion()
\r
375 var MySQLPool = new require("./mysql_pool.js");
\r
376 var pool = new MySQLPool({
\r
377 host : config.db_host,
\r
378 user : config.db_user,
\r
379 password : config.db_password,
\r
380 port : config.db_port,
\r
381 database : "webchat",
\r
383 var collection = {};
\r
384 this.IsBaned = function(ip){
\r
385 return collection[ip] == "r";
\r
387 this.IsBlockedToWrite = function(ip){
\r
388 return ip in collection;
\r
390 this.GetText = function(){
\r
392 for(var key in collection)
\r
394 if(collection[key] == "")
\r
395 text += key + "\r\n";
\r
397 text += key + ":" + collection[key] + "\r\n";
\r
401 this.Update = function(text,callfunc){
\r
405 pool.query("TRUNCATE TABLE ipbanlist",null,next);
\r
407 function(result,next){
\r
408 var items = new Array();
\r
409 lines = text.split("\r\n");
\r
410 for(var i = 0; i < lines.length; i++)
\r
412 var token = lines[i].split(":");
\r
416 if(token.length == 1)
\r
417 collection[ip] = "";
\r
419 collection[ip] = token[1];
\r
420 items.push(new Array(ip,collection[ip]));
\r
422 pool.query("INSERT INTO ipbanlist VALUES ?",[items],next);
\r
426 function GetIpBanList(callfunc)
\r
430 pool.query("SELECT * FROM ipbanlist",null,next);
\r
432 function(result,next){
\r
433 for(var i = 0; i < result.length; i++)
\r
434 collection[result[i].ip] = result[i].type;
\r
442 var ipbanlist = new IpBanCollecion();
\r
443 var $rooms = new RoomInfomationCollection();
\r
445 createLogDirectory();
\r
447 function createLogDirectory()
\r
449 fs.exists($log_directory,function(exists){
\r
450 if(exists == false)
\r
451 fs.mkdirSync($log_directory);
\r
455 function ParseConnect(socket)
\r
457 var ip = GetClientIPAdress(socket);
\r
458 console.log("connected from %s",ip);
\r
460 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
462 var room = $rooms.Get(rno);
\r
466 var roomconfig = room.GetConfig();
\r
467 roomconfig.admin = socket.handshake.admin;
\r
468 socket.json.emit("send roominfo",roomconfig);
\r
470 var romcount = room.GetRomCount();
\r
471 socket.json.emit("send romcount",romcount);
\r
472 socket.json.broadcast.emit("send romcount",romcount);
\r
474 socket.on("get pastLogList", function (msg) {
\r
475 ParseGetPastLogList(socket,msg);
\r
477 socket.on("get pastLog", function (msg) {
\r
478 ParseGetPastLog(socket,msg);
\r
480 socket.on("join",function(msg){
\r
481 ParseJoin(socket,msg);
\r
483 socket.on("quit",function(msg){
\r
484 ParseQuit(socket,msg);
\r
486 socket.on("set password",function(msg){
\r
487 ParseSetPassword(socket,msg);
\r
489 socket.on("send msg", function (msg) {
\r
490 ParseSendMsg(socket,msg);
\r
492 socket.on("disconnect", function (msg) {
\r
493 ParseDisconnect(socket,msg);
\r
497 function ParseAuthorization(handshakeData, callback)
\r
499 if(handshakeData.headers.cookie) {
\r
500 var signedCookie = cookie.parse(handshakeData.headers.cookie);
\r
501 var sessionID = connectUtils.parseSignedCookies(signedCookie, $secret)["connect.sid"];
\r
502 sessionStore.get(sessionID, function (err, session) {
\r
504 if (ipbanlist.IsBaned(handshakeData.address.address))
\r
505 result = "failed get from session store";
\r
508 else if(handshakeData.query.token != session.items.token)
\r
509 result = "invaild token";
\r
510 if(typeof(session) != "undefined" && result == null)
\r
512 handshakeData.admin = session.items.admin;
\r
513 handshakeData.sessionID = sessionID;
\r
515 callback(result,result == null && !err);
\r
518 return callback("failed get cookie", false);
\r
522 function ParseDisconnect(socket,msg)
\r
524 var ip = GetClientIPAdress(socket);
\r
525 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
526 $rooms.Get(rno).RemoveRom(ip);
\r
528 var romcount = $rooms.Get(rno).GetRomCount();
\r
529 socket.json.emit("send romcount",romcount);
\r
530 socket.json.broadcast.emit("send romcount",romcount);
\r
532 console.log("disconnected");
\r
535 function ParseSetPassword(socket,msg)
\r
537 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
542 if($rooms.Get(rno).IsVolatile() == false && $rooms.Get(rno).SetPassword(msg.owner,msg.password))
\r
543 newMeg.message = resource.password_setted_message;
\r
545 newMeg.message = resource.failed_set_password_message;
\r
546 ParseSendMsg(socket,newMeg);
\r
549 function ParseJoin(socket,msg)
\r
551 var ip = GetClientIPAdress(socket);
\r
553 if(ipbanlist.IsBlockedToWrite(ip))
\r
555 socket.emit("error",resource.block_message);
\r
559 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
561 $rooms.Get(rno).RemoveRom(ip);
\r
563 var romcount = $rooms.Get(rno).GetRomCount();
\r
564 socket.json.emit("send romcount",romcount);
\r
565 socket.json.broadcast.emit("send romcount",romcount);
\r
567 if($rooms.Get(rno).IsVolatile() == false)
\r
569 if($rooms.Get(rno).IsTimeout() ||
\r
570 $rooms.Get(rno).IsFirstAuth())
\r
572 $rooms.Get(rno).Reset(msg.name);
\r
573 ParseGetPastLog(socket,util.format($log_file_name,rno));
\r
575 else if($rooms.Get(rno).Auth(msg.name,msg.password))
\r
577 ParseGetPastLog(socket,util.format($log_file_name,rno));
\r
581 socket.emit("error",resource.unmatch_password);
\r
588 message:util.format("/enteredby %s %s %s",msg.name,msg.color,msg.mailto),
\r
590 ParseSendMsg(socket,newMeg);
\r
593 function ParseQuit(socket,msg)
\r
595 var ip = GetClientIPAdress(socket);
\r
597 if(ipbanlist.IsBlockedToWrite(ip))
\r
599 socket.emit("error",resource.block_message);
\r
603 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
607 message:resource.password_resetted_message,
\r
610 $rooms.Get(rno).AddRom(ip);
\r
612 var romcount = $rooms.Get(rno).GetRomCount();
\r
613 socket.json.emit("send romcount",romcount);
\r
614 socket.json.broadcast.emit("send romcount",romcount);
\r
616 if($rooms.Get(rno).IsVolatile() == false)
\r
618 if($rooms.Get(rno).IsOwner(msg.name))
\r
620 $rooms.Get(rno).Reset(null);
\r
621 ParseSendMsg(socket,newMeg);
\r
623 if(!$rooms.Get(rno).IsFirstAuth() &&
\r
624 !$rooms.Get(rno).IsAuthed(msg.name))
\r
627 $rooms.Get(rno).RemoveAuth(msg.name);
\r
630 newMeg.message = util.format("/quitedby %s",msg.name);
\r
631 ParseSendMsg(socket,newMeg);
\r
636 function ParseSendMsg(socket,msg)
\r
638 var ip = GetClientIPAdress(socket);
\r
640 if(ip in ipbanlist)
\r
642 socket.emit("error",resource.block_message);
\r
646 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
648 if(msg.name != $system_name &&
\r
649 $rooms.Get(rno).IsVolatile() == false &&
\r
650 !$rooms.Get(rno).IsAuthed(msg.name) &&
\r
651 !$rooms.Get(rno).IsOwner(rno,msg.name))
\r
656 var date = new Date();
\r
658 var repacked_msg = CreateMessage(msg.name,date,msg.message);
\r
660 if(socket.handshake.admin)
\r
661 repacked_msg.ip = ip;
\r
663 socket.json.emit("req msg", repacked_msg);
\r
665 socket.json.broadcast.emit("req msg", repacked_msg);
\r
667 var path = $log_directory + "/" + util.format($log_file_name,rno);
\r
668 var log = new ChatLog(path);
\r
669 log.Save(repacked_msg,ip,rno);
\r
672 function GetNameFromRoomNumber(number)
\r
674 return "/" + number;
\r
677 function GetRoomNumberFromName(name)
\r
679 if(name.charAt(0) == "/")
\r
680 return parseInt(name.substr(1));
\r
681 throw "GetRoomNumberFromName error";
\r
684 function ParseGetPastLogList(socket,msg)
\r
686 var list = fs.readdir($log_directory,function(err,files){
\r
688 var rno = GetRoomNumberFromName(socket.namespace.name);
\r
689 var pattern = $pastlogfile_pattern.replace("%d",rno);
\r
690 for(var i = 0; i < files.length; i++)
\r
692 var logname = files[i];
\r
693 if(logname.match(pattern))
\r
694 text += files[i] + "\n";
\r
696 socket.emit("req pastloglist",text);
\r
700 function ParseGetPastLog(socket,file)
\r
704 var path = $log_directory + "/" + file;
\r
705 var log = new ChatLog(path);
\r
706 log.ToArray(socket.handshake.admin,function(array){
\r
707 socket.json.emit("req pastlog",array);
\r
711 function ChatLog(path)
\r
713 this.ToArray = function(hasIp,callback)
\r
715 var state = fs.stat(path,function(err,state){
\r
718 var array = new Array();
\r
719 var stream = fs.createReadStream(path);
\r
722 .forEach(function(line){
\r
723 var msg = CreateMessageFromText(line.toString());
\r
734 this.Save = function(msg,ip,rno){
\r
735 var text = GetTextFromMessage(msg,ip);
\r
737 SplitLog(rno,function(){
\r
738 WritePastLog(path,text);
\r
742 function GetTextFromMessage(msg,ip)
\r
744 var text = msg.name + "<>" +
\r
752 function SplitLog(rno,callback)
\r
754 var state = fs.stat(path,function(err,state){
\r
755 if(err && typeof(callback) == "function")
\r
760 if(state.size > $spilt_size)
\r
762 var date = new Date();
\r
763 var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds();
\r
765 var newpath = $log_directory + "/" +
\r
766 util.format($splited_log_file_name,rno,dateString);
\r
767 fs.rename(path,newpath,callback);
\r
769 if(typeof(callback) == "function")
\r
775 function WritePastLog(path,text)
\r
778 function(callback){
\r
779 fs.open(path,"a",callback);
\r
781 function(fd,callback){
\r
782 var buf = new Buffer(text);
\r
783 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){
\r
794 function GetClientIPAdress(socket)
\r
796 return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;
\r
800 function CreateMessage(name,date,message)
\r
802 var result = {name:name,
\r
808 function CreateMessageFromText(text)
\r
810 var data = text.split("<>");
\r
811 var msg = {name:data[0],
\r