OSDN Git Service

joinとquitメッセージを新しく追加した
[webchat/WebChat.git] / chatServer.js
1 /*\r
2  * \90Ý\92è\r
3  */\r
4 $max_room_number = 1;   //\8dÅ\91å\83\8b\81[\83\80\90\94\r
5 $log_directory = "log"; //\83\8d\83O\83t\83@\83C\83\8b\82ð\92u\82­\83t\83H\83\8b\83_\81[\r
6 $log_file_name = "logfile%d.txt";       //\83\8d\83O\83t\83@\83C\83\8b\96¼(%d\82Í\82»\82Ì\82Ü\82Ü\82É\82µ\82Ä\82¨\82­\82±\82Æ)\r
7 $splited_log_file_name = "logfile%d_%s.txt"     //\95ª\8a\84\8cã\82Ì\83t\83@\83C\83\8b\96¼(%d\82Æ%s\82Í\82»\82Ì\82Ü\82Ü\82É\82µ\82Ä\82¨\82­\82±\82Æ)\r
8 $spilt_size = 1024 * 512;       //\95ª\8a\84\82·\82é\83T\83C\83Y\r
9 $block_message = "failed to send your message.";        //\83u\83\8d\83b\83N\8e\9e\82Ì\83\81\83b\83Z\81[\83W\r
10 $ip_ban_list_file_name = "ipbanlist.txt";       //\83A\83N\83Z\83X\82ð\8bÖ\8e~\82·\82éIP\82ª\8bL\98^\82³\82ê\82Ä\82¢\82é\83t\83@\83C\83\8b\r
11 $port = process.env.port || 3000;       //\83|\81[\83g\r
12 $username = "admin";    //\8aÇ\97\9d\8eÒ\97p\82Ì\83y\81[\83W\82É\83A\83N\83Z\83X\82Å\82«\82é\83\86\81[\83U\96¼\r
13 $password = "admin";    //\8aÇ\97\9d\8eÒ\97p\82Ì\83y\81[\83W\82É\83A\83N\83Z\83X\82·\82é\82Ì\82É\95K\97v\82È\83p\83X\83\8f\81[\83h\r
14 $pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //\89ß\8b\8e\83\8d\83O\82Æ\94»\92è\82·\82é\90³\8bK\95\\8c»\r
15 $logfile_pattern = "logfile[0-9]+(_*.*)?\.txt"  //\89ß\8b\8e\83\8d\83O\82Æ\94»\92è\82·\82é\90³\8bK\95\\8c»\r
16 $token_length = 32;     //\83g\81[\83N\83\93\82Ì\92·\82³\r
17 $redisHost = "localhost";       //redis\83T\81[\83o\82Ì\83A\83h\83\8c\83X\r
18 $redisPort = 6379;      //redis\83T\81[\83o\82Ì\83|\81[\83g\r
19 $redisPassword = "";    //redis\83T\81[\83o\82Ì\83p\83X\83\8f\81[\83h\r
20 $system_name = "system";        //\83V\83X\83e\83\80\94­\8c¾\82ð\95\\82·\96¼\91O\r
21 \r
22 /**\r
23  * Module dependencies.\r
24  */\r
25 \r
26 // Server\r
27 var express = require('express');\r
28 \r
29 var app = module.exports = express.createServer();\r
30 \r
31 var util = require("util");\r
32 \r
33 var lazy = require("lazy");\r
34 \r
35 var fs = require("fs");\r
36 \r
37 var parseCookie = require("connect").utils.parseCookie;\r
38 \r
39 var RedisStore = require("connect-redis")(express);\r
40 //var   sessionStore = new (require('connect').session.MemoryStore)();\r
41 var sessionStore = new RedisStore({host:$redisHost,port:$redisPort,pass:$redisPassword});\r
42 \r
43 var async = require("async");\r
44 \r
45 var path = require("path");\r
46 \r
47 // Configuration\r
48 \r
49 app.configure(function(){\r
50         app.disabled("view cache");\r
51         app.set("view options", { layout: false })\r
52         app.set("views", __dirname + "/public");\r
53         app.set("view engine", "ejs");\r
54         app.use(express.bodyParser());\r
55         app.use(express.methodOverride());\r
56         app.use(express.cookieParser());\r
57         app.use(express.session({\r
58                 store:sessionStore,\r
59                 secret: "5514EA2B-C9B2-4D65-8D81-1F33A180A0C2",\r
60                 cookie: { httpOnly: false }\r
61         }));\r
62         app.use(app.router);\r
63         app.use(express.static(__dirname + "/public"));\r
64 });\r
65 \r
66 app.configure('development', function(){\r
67   app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); \r
68 });\r
69 \r
70 app.configure('production', function(){\r
71   app.use(express.errorHandler()); \r
72 });\r
73 \r
74 // Routes\r
75 app.get("/chat", function(req, res){\r
76         var auth_string = getRandomString($token_length);\r
77         req.session.items = {token:auth_string};\r
78 \r
79         var room_number = 0;\r
80         if(typeof(req.query.rno) != "undefined")\r
81                 room_number = req.query.rno;\r
82         res.render("chat",{rno:room_number,token:auth_string});\r
83 });\r
84 \r
85 app.all("/log/" + $logfile_pattern,express.basicAuth(function (user, pass) {\r
86         return user === $username && pass === $password;\r
87 }));\r
88 \r
89 app.get("/log/" + $logfile_pattern, function(req, res){\r
90         res.sendfile(__dirname + req.url);\r
91 });\r
92 \r
93 app.all("/admin",express.basicAuth(function (user, pass) {\r
94         return user === $username && pass === $password;\r
95 }));\r
96 \r
97 app.get("/admin", function(req, res){\r
98         renderAdmin(req,res);\r
99 });\r
100 \r
101 app.post("/admin",function(req,res){\r
102         if(req.session.items.token != req.body.token)\r
103         {\r
104                 res.send($invaild_token_message);\r
105                 return;\r
106         }\r
107         if(typeof(req.body.erase) != "undefined")\r
108         {\r
109                 removeLog(req.body.file,function(){\r
110                         renderAdmin(req,res);\r
111                 });\r
112         }\r
113         if(typeof(req.body.registor) != "undefined")\r
114         {\r
115                 updateIpBanList(req.body.newbanlist,function(){\r
116                         renderAdmin(req,res);\r
117                 });\r
118         }\r
119 });\r
120 \r
121 function renderAdmin(req,res)\r
122 {\r
123         var auth_string = getRandomString($token_length);\r
124         req.session.items = {token:auth_string};\r
125 \r
126         fs.readdir($log_directory,function(err,list){\r
127                 res.render("admin", {\r
128                         files: list,\r
129                         log_directory:$log_directory,\r
130                         ipbanlist:getTextFromIpBanlist(ipbanlist),\r
131                         token:auth_string\r
132                 });\r
133         });\r
134 }\r
135 \r
136 function getRandomString(length)\r
137 {\r
138         var RandomString = "";\r
139         var BaseString ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()=~|-^\@[;:],./\`{+*}<>?_";\r
140         for(var i=0; i<length; i++) {\r
141                 RandomString += BaseString.charAt( Math.floor( Math.random() * BaseString.length));\r
142         }\r
143         return RandomString\r
144 }\r
145 \r
146 function removeLog(files,callback)\r
147 {\r
148         async.map(files,\r
149         function(item,callback){\r
150                 fs.unlink($log_directory + "/" + item,callback);\r
151         },\r
152         function(err,results){\r
153                 if(typeof(callback) == "function")\r
154                         callback();\r
155         });\r
156 }\r
157 \r
158 function getTextFromIpBanlist(list)\r
159 {\r
160         var text = "";\r
161         for(var key in ipbanlist)\r
162         {\r
163                 if(ipbanlist[key] == "")\r
164                         text += key + "\r\n";\r
165                 else\r
166                         text += key + ":" + ipbanlist[key] + "\r\n";\r
167         }\r
168         return text;\r
169 }\r
170 \r
171 function updateIpBanList(text,callfunc)\r
172 {\r
173         async.waterfall([\r
174                 function(callback){\r
175                         fs.open($ip_ban_list_file_name,"w",callback);\r
176                 },\r
177                 function(fd,callback){\r
178                         var buf = new Buffer(text);\r
179                         fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
180                                 callback(null,fd);\r
181                         });\r
182                 },\r
183                 function(fd,callback){\r
184                         fs.close(fd,function(){\r
185                                 getIpBanList(callfunc);\r
186                         });\r
187                 }\r
188         ]);\r
189 }\r
190 \r
191 app.listen($port);\r
192 console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);\r
193 \r
194 /*\r
195  * \83T\81[\83o\81[\95\94\95ª\r
196  */\r
197 \r
198 var io = require("socket.io").listen(app);\r
199 io.configure('production', function(){\r
200   io.enable('browser client minification');  // minified \82³\82ê\82½\83N\83\89\83C\83A\83\93\83g\83t\83@\83C\83\8b\82ð\91\97\90M\82·\82é\r
201   io.enable('browser client etag');          // \83o\81[\83W\83\87\83\93\82É\82æ\82Á\82Ä etag \82É\82æ\82é\83L\83\83\83b\83V\83\93\83O\82ð\97L\8cø\82É\82·\82é\r
202   io.set('log level', 1);                    // \83\8d\83O\83\8c\83x\83\8b\82ð\90Ý\92è(\83f\83t\83H\83\8b\83g\82æ\82è\89º\82°\82Ä\82¢\82é)\r
203 });\r
204 var clients = new Array();\r
205 \r
206 var ipbanlist = {};\r
207 \r
208 createLogDirectory();\r
209 \r
210 getIpBanList();\r
211 \r
212 for(var i = 0; i < $max_room_number; i++)\r
213 {\r
214         clients[i] =io\r
215         .of(GetNameFromRoomNumber(i))\r
216         .authorization(ParseAuthorization)\r
217         .on("connection", function (socket) {\r
218                 console.log("connected from %s",GetClientIPAdress(socket));\r
219                 socket.on("get pastLogList", function (msg) {\r
220                         ParseGetPastLogList(socket,msg);\r
221                 });\r
222                 socket.on("get pastLog", function (msg) {\r
223                         ParseGetPastLog(socket,msg);\r
224                 });\r
225                 socket.on("join",function(msg){\r
226                         ParseJoin(socket,msg);\r
227                 });\r
228                 socket.on("quit",function(msg){\r
229                         ParseQuit(socket,msg);\r
230                 });\r
231                 socket.on("send msg", function (msg) {\r
232                         ParseSendMsg(socket,msg);\r
233                 });\r
234                 socket.on("disconnect", function (msg) {\r
235                         ParseDisconnect(socket,msg);\r
236                 });\r
237         });\r
238 }\r
239 \r
240 function createLogDirectory()\r
241 {\r
242         path.exists($log_directory,function(exists){\r
243                 if(exists == false)\r
244                         fs.mkdirSync($log_directory);\r
245         });\r
246 }\r
247 \r
248 function getIpBanList(callback)\r
249 {\r
250         ipbanlist = {};\r
251         path.exists($ip_ban_list_file_name,function(exists){\r
252                 if(exists == false)\r
253                 {\r
254                         if(typeof(callback) == "function")\r
255                                 callback();\r
256                         return;\r
257                 }\r
258                 var stream = fs.createReadStream($ip_ban_list_file_name);\r
259                 new lazy(stream)\r
260                         .lines\r
261                         .forEach(function(line){\r
262                                 var token = line.toString().split(":");\r
263                                 var ip = token[0].replace(/(\r|\n|\r\n)/gm, "");\r
264                                 if(token.length == 1)\r
265                                         ipbanlist[ip] = "";\r
266                                 else\r
267                                         ipbanlist[ip] = token[1];\r
268                         })\r
269                         .join(function(){\r
270                                 if(typeof(callback) == "function")\r
271                                         callback();\r
272                         });\r
273         });\r
274 }\r
275 \r
276 function ParseAuthorization(handshakeData, callback)\r
277 {\r
278         if(handshakeData.headers.cookie) {\r
279                 var cookie = handshakeData.headers.cookie;\r
280                 var sessionID = parseCookie(cookie)["connect.sid"];\r
281                 sessionStore.get(sessionID, function (err, session) {\r
282                         var result = null;\r
283                         if (err || ipbanlist[handshakeData.address.address] == "r")\r
284                                 result = "failed get from session store";\r
285                         else if(handshakeData.query.token != session.items.token)\r
286                                 result = "invaild token";\r
287                         sessionStore.destroy(sessionID);\r
288                         callback(result,result == null && !err);\r
289                 });\r
290         } else {\r
291                 return callback("failed get cookie", false);\r
292         }\r
293 }\r
294 \r
295 function ParseDisconnect(socket,msg)\r
296 {\r
297         console.log("disconnected");\r
298 }\r
299 \r
300 function ParseJoin(socket,msg)\r
301 {\r
302         var ip = GetClientIPAdress(socket);\r
303 \r
304         if(ip in ipbanlist)\r
305         {\r
306                 socket.emit("error",$block_message);\r
307                 return;\r
308         }\r
309 \r
310         var newMeg = {\r
311                 name:$system_name,\r
312                 message:util.format("/enteredby %s %s %s",msg.name,msg.color,msg.mailto),\r
313         };\r
314         ParseSendMsg(socket,newMeg);\r
315 }\r
316 \r
317 function ParseQuit(socket,msg)\r
318 {\r
319         var ip = GetClientIPAdress(socket);\r
320 \r
321         if(ip in ipbanlist)\r
322         {\r
323                 socket.emit("error",$block_message);\r
324                 return;\r
325         }\r
326 \r
327         var newMeg = {\r
328                 name:$system_name,\r
329                 message:util.format("/quitedby %s",msg.name),\r
330         };\r
331         ParseSendMsg(socket,newMeg);\r
332 }\r
333 \r
334 //socket \90Ú\91±\92\86\82Ì\83\\83P\83b\83g\r
335 //msg msg\83N\83\89\83X\r
336 function ParseSendMsg(socket,msg)\r
337 {\r
338         var ip = GetClientIPAdress(socket);\r
339 \r
340         if(ip in ipbanlist)\r
341         {\r
342                 socket.emit("error",$block_message);\r
343                 return;\r
344         }\r
345 \r
346         var date = new Date();\r
347 \r
348         var repacked_msg = CreateMessage(msg.name,date,msg.message);\r
349 \r
350         socket.json.emit("req msg", repacked_msg);\r
351 \r
352         socket.json.broadcast.emit("req msg", repacked_msg);\r
353 \r
354         var rno = GetRoomNumberFromName(socket.namespace.name);\r
355         var path = $log_directory + "/" + util.format($log_file_name,rno);\r
356         var log = new ChatLog(path);\r
357         log.Save(repacked_msg,ip,rno);\r
358 }\r
359 \r
360 function GetNameFromRoomNumber(number)\r
361 {\r
362         return "/" + number;\r
363 }\r
364 \r
365 function GetRoomNumberFromName(name)\r
366 {\r
367         if(name.charAt(0) == "/")\r
368                 return parseInt(name.substr(1));\r
369         throw "GetRoomNumberFromName error";\r
370 }\r
371 \r
372 function ParseGetPastLogList(socket,msg)\r
373 {\r
374         var list = fs.readdir($log_directory,function(err,files){\r
375                 var text = "";\r
376                 var rno = GetRoomNumberFromName(socket.namespace.name);\r
377                 var pattern = $pastlogfile_pattern.replace("%d",rno);\r
378                 for(var i = 0; i < files.length; i++)\r
379                 {\r
380                         var logname = files[i];\r
381                         if(logname.match(pattern))\r
382                                 text += files[i] + "\n";\r
383                 }\r
384                 socket.emit("req pastloglist",text);\r
385         });\r
386 }\r
387 \r
388 function ParseGetPastLog(socket,file)\r
389 {\r
390         if(file == "")\r
391                 return;\r
392         var path = $log_directory + "/" + file;\r
393         var log = new ChatLog(path);\r
394         log.ToArray(function(array){\r
395                 socket.json.emit("req pastlog",array);\r
396         });\r
397 }\r
398 \r
399 function ChatLog(path)\r
400 {\r
401         this.ToArray = function(callback)\r
402         {\r
403                 var state = fs.stat(path,function(err,state){\r
404                         if(err)\r
405                                 return;\r
406                         var array = new Array();\r
407                         var stream = fs.createReadStream(path);\r
408                         new lazy(stream)\r
409                                 .lines\r
410                                 .forEach(function(line){\r
411                                         array.push(CreateMessageFromText(line.toString()));\r
412                                 })\r
413                                 .join(function(){\r
414                                         callback(array);\r
415                                 });\r
416                 });\r
417         }\r
418 \r
419         this.Save = function(msg,ip,rno){\r
420                 var text = GetTextFromMessage(msg,ip);\r
421 \r
422                 SplitLog(rno,function(){\r
423                         WritePastLog(path,text);\r
424                 });\r
425         };\r
426 \r
427         function GetTextFromMessage(msg,ip)\r
428         {\r
429                 var text = msg.name + "<>" +\r
430                                 msg.date + "<>" +\r
431                                 ip + "<>" +\r
432                                 msg.message +\r
433                                 "\n";\r
434                 return text;\r
435         }\r
436 \r
437         function SplitLog(rno,callback)\r
438         {\r
439                 var state = fs.stat(path,function(err,state){\r
440                         if(err && typeof(callback) == "function")\r
441                         {\r
442                                 callback();\r
443                                 return;\r
444                         }\r
445                         if(state.size > $spilt_size)\r
446                         {\r
447                                 var date = new Date();\r
448                                 var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds();\r
449 \r
450                                 var newpath = $log_directory + "/" +\r
451                                         util.format($splited_log_file_name,rno,dateString);\r
452                                 fs.rename(path,newpath,callback);\r
453                         }else{\r
454                                 if(typeof(callback) == "function")\r
455                                         callback();\r
456                         }\r
457                 });\r
458         }\r
459 \r
460         function WritePastLog(path,text)\r
461         {\r
462                 async.waterfall([\r
463                         function(callback){\r
464                                 fs.open(path,"a",callback);\r
465                         },\r
466                         function(fd,callback){\r
467                                 var buf = new Buffer(text);\r
468                                 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
469                                         callback(null,fd);\r
470                                 });\r
471                         },\r
472                         function(fd){\r
473                                 fs.close(fd);\r
474                         }\r
475                 ]);\r
476         }\r
477 }\r
478 \r
479 function GetClientIPAdress(socket)\r
480 {\r
481         return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;\r
482 }\r
483 \r
484 // Message \83N\83\89\83X\r
485 function CreateMessage(name,date,message)\r
486 {\r
487         var result = {name:name,\r
488                 date:date,\r
489                 message:message};\r
490         return result;\r
491 }\r
492 function CreateMessageFromText(text)\r
493 {\r
494         var data = text.split("<>");\r
495         var msg = {name:data[0],\r
496                 date:data[1],\r
497                 message:data[3]};\r
498         return msg;\r
499 }\r