X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=chatServer.js;h=78dad96a04dfef7da7194a03c992badc02ccf5a0;hb=5a3d56ea11a87a34b4c1f7ae70effd62a1850aca;hp=461a3c227007074762a992d15d84b9b82e3feec2;hpb=60f6be14cc5d6c9a138f1108ef83b3b1eabfbeaa;p=webchat%2FWebChat.git diff --git a/chatServer.js b/chatServer.js index 461a3c2..78dad96 100644 --- a/chatServer.js +++ b/chatServer.js @@ -1,22 +1,30 @@ -/* - * Ý’è +/* + * 設定 */ -$max_room_number = 1; //Å‘僋[ƒ€” -$log_directory = "log"; //ƒƒOƒtƒ@ƒCƒ‹‚ð’u‚­ƒtƒHƒ‹ƒ_[ -$log_file_name = "logfile%d.txt"; //ƒƒOƒtƒ@ƒCƒ‹–¼(%d‚Í‚»‚Ì‚Ü‚Ü‚É‚µ‚Ä‚¨‚­‚±‚Æ) -$splited_log_file_name = "logfile%d_%s.txt" //•ªŠ„Œã‚̃tƒ@ƒCƒ‹–¼(%d‚Æ%s‚Í‚»‚Ì‚Ü‚Ü‚É‚µ‚Ä‚¨‚­‚±‚Æ) -$spilt_size = 1024 * 512; //•ªŠ„‚·‚éƒTƒCƒY -$block_message = "failed to send your message."; //ƒuƒƒbƒNŽž‚̃ƒbƒZ[ƒW -$ip_ban_list_file_name = "ipbanlist.txt"; //ƒAƒNƒZƒX‚ð‹ÖŽ~‚·‚éIP‚ª‹L˜^‚³‚ê‚Ä‚¢‚éƒtƒ@ƒCƒ‹ -$port = process.env.port || 3000; //ƒ|[ƒg -$username = "admin"; //ŠÇ—ŽÒ—p‚̃y[ƒW‚ɃAƒNƒZƒX‚Å‚«‚郆[ƒU–¼ -$password = "admin"; //ŠÇ—ŽÒ—p‚̃y[ƒW‚ɃAƒNƒZƒX‚·‚é‚Ì‚É•K—v‚ȃpƒXƒ[ƒh -$pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //‰ß‹ŽƒƒO‚Æ”»’è‚·‚鐳‹K•\Œ» -$logfile_pattern = "logfile[0-9]+(_*.*)?\.txt" //‰ß‹ŽƒƒO‚Æ”»’è‚·‚鐳‹K•\Œ» -$token_length = 16; //ƒg[ƒNƒ“‚Ì’·‚³ -$redisHost = "localhost"; //redisƒT[ƒo‚̃AƒhƒŒƒX -$redisPort = 6379; //redisƒT[ƒo‚̃|[ƒg -$redisPassword = ""; //redisƒT[ƒo‚̃pƒXƒ[ƒh +$max_room_number = 3; //最大ルーム数 +$spilt_size = 1024 * 512; //分割するサイズ +$reset_password_diff = 1000 * 60 * 60; //ルームパスワードをリセットする間隔 +$gc_time_interval = 1000 * 60 * 60; //ゴミ掃除を行う間隔 +$block_message = "メッセージの送信に失敗しました"; //ブロック時のメッセージ +$not_match_password = "パスワードが一致しませんでした"; //パスワードが一致しない場合に表示されるメッセージ +$password_setted_message = "パスワードを設定しました"; //パスワードが設定されたときに表示されるメッセージ +$password_resetted_message = "パスワードをリセットしました"; //パスワードが再設定されたときに表示されるメッセージ +$failed_set_password_message = "パスワードの設定に失敗しました"; //パスワードが再設定されたときに表示されるメッセージ +$ip_ban_list_file_name = "ipbanlist.txt"; //アクセスを禁止するIPが記録されているファイル +$room_configure_file_name = "roomlist.txt"; //ルームの設定が記録されているファイル +$port = process.env.port || 3000; //ポート +$username = "admin"; //管理者用のページにアクセスできるユーザ名 +$password = "admin"; //管理者用のページにアクセスするのに必要なパスワード +$token_length = 32; //トークンの長さ +$redisHost = "localhost"; //redisサーバのアドレス +$redisPort = 6379; //redisサーバのポート +$redisPassword = ""; //redisサーバのパスワード +$system_name = "system"; //システム発言を表す名前 +$log_directory = "log"; //ログファイルを置くフォルダー +$log_file_name = "logfile%d.txt"; //ログファイル名(%dはそのままにしておくこと) +$splited_log_file_name = "logfile%d_%s.txt" //分割後のファイル名(%dと%sはそのままにしておくこと) +$pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //過去ログと判定する正規表現 +$logfile_pattern = "logfile[0-9]+(_*.*)?\.txt" //過去ログと判定する正規表現 /** * Module dependencies. @@ -36,7 +44,6 @@ var fs = require("fs"); var parseCookie = require("connect").utils.parseCookie; var RedisStore = require("connect-redis")(express); -//var sessionStore = new (require('connect').session.MemoryStore)(); var sessionStore = new RedisStore({host:$redisHost,port:$redisPort,pass:$redisPassword}); var async = require("async"); @@ -111,7 +118,13 @@ app.post("/admin",function(req,res){ } if(typeof(req.body.registor) != "undefined") { - updateIpBanList(req.body.newbanlist,function(){ + ipbanlist.Update(req.body.newbanlist,function(){ + renderAdmin(req,res); + }); + } + if(typeof(req.body.updateroom) != "undefined") + { + $rooms.Update(req.body.newroomlist,function(){ renderAdmin(req,res); }); } @@ -121,13 +134,15 @@ function renderAdmin(req,res) { var auth_string = getRandomString($token_length); req.session.items = {token:auth_string}; + var iplist = ipbanlist.GetText(); fs.readdir($log_directory,function(err,list){ res.render("admin", { files: list, log_directory:$log_directory, - ipbanlist:getTextFromIpBanlist(ipbanlist), - token:auth_string + ipbanlist:iplist, + token:auth_string, + roomlist:$rooms.GetString() }); }); } @@ -154,73 +169,72 @@ function removeLog(files,callback) }); } -function getTextFromIpBanlist(list) -{ - var text = ""; - for(var key in ipbanlist) - { - if(ipbanlist[key] == "") - text += key + "\r\n"; - else - text += key + ":" + ipbanlist[key] + "\r\n"; - } - return text; -} - -function updateIpBanList(text,callfunc) -{ - async.waterfall([ - function(callback){ - fs.open($ip_ban_list_file_name,"w",callback); - }, - function(fd,callback){ - var buf = new Buffer(text); - fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){ - callback(null,fd); - }); - }, - function(fd,callback){ - fs.close(fd,function(){ - getIpBanList(callfunc); - }); - } - ]); -} - app.listen($port); console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); /* - * ƒT[ƒo[•”•ª + * サーバー部分 */ var io = require("socket.io").listen(app); io.configure('production', function(){ - io.enable('browser client minification'); // minified ‚³‚ꂽƒNƒ‰ƒCƒAƒ“ƒgƒtƒ@ƒCƒ‹‚𑗐M‚·‚é - io.enable('browser client etag'); // ƒo[ƒWƒ‡ƒ“‚É‚æ‚Á‚Ä etag ‚É‚æ‚éƒLƒƒƒbƒVƒ“ƒO‚ð—LŒø‚É‚·‚é - io.set('log level', 1); // ƒƒOƒŒƒxƒ‹‚ðÝ’è(ƒfƒtƒHƒ‹ƒg‚æ‚艺‚°‚Ä‚¢‚é) + io.enable('browser client minification'); // minified されたクライアントファイルを送信する + io.enable('browser client etag'); // バージョンによって etag によるキャッシングを有効にする + io.set('log level', 1); // ログレベルを設定(デフォルトより下げている) }); var clients = new Array(); -var ipbanlist = {}; +var ipbanlist = new IpBanCollecion(); +var $rooms = new RoomInfomationCollection(); createLogDirectory(); -getIpBanList(); - for(var i = 0; i < $max_room_number; i++) { clients[i] =io .of(GetNameFromRoomNumber(i)) .authorization(ParseAuthorization) .on("connection", function (socket) { - console.log("connected from %s",GetClientIPAdress(socket)); + var ip = GetClientIPAdress(socket); + + console.log("connected from %s",ip); + + var rno = GetRoomNumberFromName(socket.namespace.name); + var roomconfig = {}; + $rooms.Get(rno).AddRom(ip); + if($rooms.Get(rno).IsVolatile() == false) + { + if($rooms.Get(rno).IsFixedPassword()) + roomconfig.type = 2; + else if($rooms.Get(rno).IsHiddenLogFromRom()) + roomconfig.type = 3; + else + roomconfig.type = 1; + roomconfig.IsOwned = !$rooms.Get(rno).IsFirstAuth(); + }else{ + roomconfig.type = 0; + } + socket.json.emit("send roominfo",roomconfig); + + var romcount = $rooms.Get(rno).GetRomCount(); + socket.json.emit("send romcount",romcount); + socket.json.broadcast.emit("send romcount",romcount); + socket.on("get pastLogList", function (msg) { ParseGetPastLogList(socket,msg); }); socket.on("get pastLog", function (msg) { ParseGetPastLog(socket,msg); }); + socket.on("join",function(msg){ + ParseJoin(socket,msg); + }); + socket.on("quit",function(msg){ + ParseQuit(socket,msg); + }); + socket.on("set password",function(msg){ + ParseSetPassword(socket,msg); + }); socket.on("send msg", function (msg) { ParseSendMsg(socket,msg); }); @@ -238,34 +252,6 @@ function createLogDirectory() }); } -function getIpBanList(callback) -{ - ipbanlist = {}; - path.exists($ip_ban_list_file_name,function(exists){ - if(exists == false) - { - if(typeof(callback) == "function") - callback(); - return; - } - var stream = fs.createReadStream($ip_ban_list_file_name); - new lazy(stream) - .lines - .forEach(function(line){ - var token = line.toString().split(":"); - var ip = token[0].replace(/(\r|\n|\r\n)/gm, ""); - if(token.length == 1) - ipbanlist[ip] = ""; - else - ipbanlist[ip] = token[1]; - }) - .join(function(){ - if(typeof(callback) == "function") - callback(); - }); - }); -} - function ParseAuthorization(handshakeData, callback) { if(handshakeData.headers.cookie) { @@ -273,12 +259,14 @@ function ParseAuthorization(handshakeData, callback) var sessionID = parseCookie(cookie)["connect.sid"]; sessionStore.get(sessionID, function (err, session) { var result = null; - if (err || ipbanlist[handshakeData.address.address] == "r") + if (ipbanlist.IsBaned(handshakeData.address.address)) result = "failed get from session store"; + else if(err) + result = err; else if(handshakeData.query.token != session.items.token) result = "invaild token"; sessionStore.destroy(sessionID); - callback(result,result == null); + callback(result,result == null && !err); }); } else { return callback("failed get cookie", false); @@ -287,11 +275,118 @@ function ParseAuthorization(handshakeData, callback) function ParseDisconnect(socket,msg) { + var ip = GetClientIPAdress(socket); + var rno = GetRoomNumberFromName(socket.namespace.name); + $rooms.Get(rno).RemoveRom(ip); + + var romcount = $rooms.Get(rno).GetRomCount(); + socket.json.emit("send romcount",romcount); + socket.json.broadcast.emit("send romcount",romcount); + console.log("disconnected"); } -//socket Ú‘±’†‚̃\ƒPƒbƒg -//msg msgƒNƒ‰ƒX +function ParseSetPassword(socket,msg) +{ + var rno = GetRoomNumberFromName(socket.namespace.name); + var newMeg = { + name:$system_name, + message:null, + }; + if($rooms.Get(rno).IsVolatile() == false && $rooms.Get(rno).SetPassword(msg.owner,msg.password)) + newMeg.message = $password_setted_message; + else + newMeg.message = $failed_set_password_message; + ParseSendMsg(socket,newMeg); +} + +function ParseJoin(socket,msg) +{ + var ip = GetClientIPAdress(socket); + + if(ipbanlist.IsBlockedToWrite(ip)) + { + socket.emit("error",$block_message); + return; + } + + var rno = GetRoomNumberFromName(socket.namespace.name); + + $rooms.Get(rno).RemoveRom(ip); + + var romcount = $rooms.Get(rno).GetRomCount(); + socket.json.emit("send romcount",romcount); + socket.json.broadcast.emit("send romcount",romcount); + + if($rooms.Get(rno).IsVolatile() == false) + { + if($rooms.Get(rno).IsTimeout() || + $rooms.Get(rno).IsFirstAuth()) + { + $rooms.Get(rno).Reset(msg.name); + ParseGetPastLog(socket,util.format($log_file_name,rno)); + } + else if($rooms.Get(rno).Auth(msg.name,msg.password)) + { + ParseGetPastLog(socket,util.format($log_file_name,rno)); + } + else + { + socket.emit("error",$not_match_password); + return; + } + } + + var newMeg = { + name:$system_name, + message:util.format("/enteredby %s %s %s",msg.name,msg.color,msg.mailto), + }; + ParseSendMsg(socket,newMeg); +} + +function ParseQuit(socket,msg) +{ + var ip = GetClientIPAdress(socket); + + if(ipbanlist.IsBlockedToWrite(ip)) + { + socket.emit("error",$block_message); + return; + } + + var rno = GetRoomNumberFromName(socket.namespace.name); + + var newMeg = { + name:$system_name, + message:$password_resetted_message, + }; + + $rooms.Get(rno).AddRom(ip); + + var romcount = $rooms.Get(rno).GetRomCount(); + socket.json.emit("send romcount",romcount); + socket.json.broadcast.emit("send romcount",romcount); + + if($rooms.Get(rno).IsVolatile() == false) + { + if($rooms.Get(rno).IsOwner(msg.name)) + { + $rooms.Get(rno).Reset(null); + ParseSendMsg(socket,newMeg); + } + if(!$rooms.Get(rno).IsFirstAuth() && + !$rooms.Get(rno).IsAuthed(msg.name)) + return; + else + $rooms.Get(rno).RemoveAuth(msg.name); + } + + newMeg.message = util.format("/quitedby %s",msg.name); + ParseSendMsg(socket,newMeg); +} + +//socket 接続中のソケット +//msg msgクラス function ParseSendMsg(socket,msg) { var ip = GetClientIPAdress(socket); @@ -302,19 +397,27 @@ function ParseSendMsg(socket,msg) return; } + var rno = GetRoomNumberFromName(socket.namespace.name); + + if(msg.name != $system_name && + $rooms.Get(rno).IsVolatile() == false && + !$rooms.Get(rno).IsAuthed(msg.name) && + !$rooms.Get(rno).IsOwner(rno,msg.name)) + { + return; + } + var date = new Date(); - //var dateString = date.toFormat("YYYY/MM/DD HH24:MI:SS"); - var repacked_msg = CreateMessage(msg,date); + var repacked_msg = CreateMessage(msg.name,date,msg.message); socket.json.emit("req msg", repacked_msg); socket.json.broadcast.emit("req msg", repacked_msg); - var rno = GetRoomNumberFromName(socket.namespace.name); var path = $log_directory + "/" + util.format($log_file_name,rno); var log = new ChatLog(path); - log.Save(repacked_msg,ip); + log.Save(repacked_msg,ip,rno); } function GetNameFromRoomNumber(number) @@ -376,10 +479,10 @@ function ChatLog(path) }); } - this.Save = function(msg,ip){ + this.Save = function(msg,ip,rno){ var text = GetTextFromMessage(msg,ip); - SplitLog(path,function(){ + SplitLog(rno,function(){ WritePastLog(path,text); }); }; @@ -394,7 +497,7 @@ function ChatLog(path) return text; } - function SplitLog(path,callback) + function SplitLog(rno,callback) { var state = fs.stat(path,function(err,state){ if(err && typeof(callback) == "function") @@ -405,9 +508,10 @@ function ChatLog(path) if(state.size > $spilt_size) { var date = new Date(); + var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds(); var newpath = $log_directory + "/" + - util.format($splited_log_file_name,rno,date.toFormat("YYYYMMDDHH24MISS")); + util.format($splited_log_file_name,rno,dateString); fs.rename(path,newpath,callback); }else{ if(typeof(callback) == "function") @@ -440,12 +544,12 @@ function GetClientIPAdress(socket) return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address; } -// Message ƒNƒ‰ƒX -function CreateMessage(msg,date) +// Message クラス +function CreateMessage(name,date,message) { - var result = {name:msg.name, + var result = {name:name, date:date, - message:msg.message}; + message:message}; return result; } function CreateMessageFromText(text) @@ -456,3 +560,278 @@ function CreateMessageFromText(text) message:data[3]}; return msg; } + +//RoomInfomationCollecionクラス +function RoomInfomationCollection() +{ + var collection = {}; + this.Get = function(rno){ + return collection[rno]; + } + this.IsContains = function(rno){ + return rno in collection; + }; + this.GetString = function(){ + var retval = ""; + for(var rno in collection) + { + if($rooms.Get(rno).IsVolatile()) + continue; + var pass = collection[rno].password; + if(pass == null) + pass = ""; + var hiddenlog = collection[rno].hiddenlog; + retval += rno + ":" + pass + ":" + hiddenlog + "\r\n"; + } + return retval; + }; + this.GetKeys = function(){ + var retval = {}; + for(var rno in collection) + { + retval[rno] = {}; + } + return retval; + } + this.Update = function(text,callfunc){ + async.waterfall([ + function(callback){ + fs.open($room_configure_file_name,"w",callback); + }, + function(fd,callback){ + var buf = new Buffer(text); + fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){ + callback(null,fd); + }); + }, + function(fd,callback){ + fs.close(fd,function(){ + GetRoomList(callfunc); + }); + } + ]); + } + function GetRoomList(callback){ + Clear(); + path.exists($room_configure_file_name,function(exists){ + if(exists == false) + { + if(typeof(callback) == "function") + callback(); + return; + } + var stream = fs.createReadStream($room_configure_file_name); + new lazy(stream) + .lines + .forEach(function(line){ + var token = line.toString().replace(/(\r|\n|\r\n)/gm, "").split(":"); + if(token.length == 1) + { + Add(token[0],null,false); + } + else if(token.length == 2) + { + var rno = token[0]; + var pass = token[1]; + if(pass == "") + pass = null; + Add(rno, pass,false); + } + else if(token.length == 3) + { + var rno = token[0]; + var pass = token[1]; + if(pass == "") + pass = null; + var hiddenlog = false; + if(token[2] == "true") + hiddenlog = true; + Add(rno, pass,hiddenlog); + } + }) + .join(function(){ + if(typeof(callback) == "function") + callback(); + }); + }); + } + function Clear(){ + collection = {}; + for(var i = 0; i < $max_room_number; i++) + Add(i,null,null); + }; + function Add(rno,pass,hiddenlogflag){ + collection[rno] = new RoomInfomation(pass,hiddenlogflag); + if(pass != null) + collection[rno].owner = $system_name; + }; + var $gc_interval_id = setInterval(function(){ + for(var rno in this.rom_list) + collection[rno].GCRomList(); + },$gc_time_interval); + GetRoomList(); +} + +//RoomInfomationクラス +function RoomInfomation(pass,hiddenlogflag) +{ + this.password = pass; + this.rom_list = {}; + this.authed_list = {}; + this.owner = null; + this.time = null; + this.hiddenlog = hiddenlogflag; + this.IsVolatile = function(){ + return this.owner == null && + this.password == null && + this.time == null && + this.hiddenlog == null; + } + this.GetRomCount = function(){ + var count = 0; + for(var key in this.rom_list) + count++; + return count; + }; + this.AddRom = function(ip){ + var date = new Date(); + this.rom_list[ip] = {time:date.getTime()}; + }; + this.RemoveRom = function(ip){ + delete this.rom_list[ip]; + }; + this.Reset = function(owner){ + var date = new Date(); + var time = date.getTime(); + this.password = null; + this.authed_list = {}; + this.owner = owner; + this.time = time; + }; + this.IsFirstAuth = function(){ + return this.owner == null; + }; + this.IsAuthed = function(name){ + return name == this.owner || + name in this.authed_list; + }; + this.IsHiddenLogFromRom = function(){ + return this.hiddenlog; + }; + this.IsFixedPassword = function(){ + return this.owner == $system_name; + }; + this.IsOwner = function(name){ + return this.owner == name; + }; + this.IsTimeout = function(){ + var date = new Date(); + var current_time = date.getTime(); + return !this.IsFixedPassword() && + current_time - this.time >= $reset_password_diff; + }; + this.RemoveAuth = function(name) + { + delete this.authed_list[name]; + }; + this.Auth = function(name,password){ + if(this.password != password) + return false; + var date = new Date(); + var time = date.getTime(); + this.time = time; + this.authed_list[name] = ""; + return true; + }; + this.SetPassword = function(owner,password){ + if(owner == this.owner && + !this.IsFixedPassword() && + !this.IsHiddenLogFromRom()) + { + var date = new Date(); + this.time = date.getTime(); + this.password = password; + return true; + } + return false; + }; + this.GCRomList = function(){ + var date = new Date(); + var current_time = date.getTime(); + for(var ip in this.rom_list) + { + if(current_time - this.rom_list[ip].time >= $gc_time_interval) + delete this.rom_list[ip]; + } + }; +} + +//IPBANクラス +function IpBanCollecion() +{ + var collection = {}; + this.IsBaned = function(ip){ + return collection[ip] == "r"; + } + this.IsBlockedToWrite = function(ip){ + return ip in collection; + } + this.GetText = function(){ + var text = ""; + for(var key in collection) + { + if(collection[key] == "") + text += key + "\r\n"; + else + text += key + ":" + collection[key] + "\r\n"; + } + return text; + } + this.Update = function(text,callfunc){ + async.waterfall([ + function(callback){ + fs.open($ip_ban_list_file_name,"w",callback); + }, + function(fd,callback){ + var buf = new Buffer(text); + fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){ + callback(null,fd); + }); + }, + function(fd,callback){ + fs.close(fd,function(){ + GetIpBanList(callfunc); + }); + } + ]); + } + function GetIpBanList(callback) + { + collection = {}; + path.exists($ip_ban_list_file_name,function(exists){ + if(exists == false) + { + if(typeof(callback) == "function") + callback(); + return; + } + var stream = fs.createReadStream($ip_ban_list_file_name); + new lazy(stream) + .lines + .forEach(function(line){ + var token = line.toString().replace(/(\r|\n|\r\n)/gm, "").split(":"); + var ip = token[0]; + if(token.length == 1) + collection[ip] = ""; + else + collection[ip] = token[1]; + }) + .join(function(){ + if(typeof(callback) == "function") + callback(); + }); + }); + } + GetIpBanList(); +} +