OSDN Git Service

ログ分割時に落ちる不具合を修正した
[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 \r
21 /**\r
22  * Module dependencies.\r
23  */\r
24 \r
25 // Server\r
26 var express = require('express');\r
27 \r
28 var app = module.exports = express.createServer();\r
29 \r
30 var util = require("util");\r
31 \r
32 var lazy = require("lazy");\r
33 \r
34 var fs = require("fs");\r
35 \r
36 var parseCookie = require("connect").utils.parseCookie;\r
37 \r
38 var RedisStore = require("connect-redis")(express);\r
39 //var   sessionStore = new (require('connect').session.MemoryStore)();\r
40 var sessionStore = new RedisStore({host:$redisHost,port:$redisPort,pass:$redisPassword});\r
41 \r
42 var async = require("async");\r
43 \r
44 var path = require("path");\r
45 \r
46 // Configuration\r
47 \r
48 app.configure(function(){\r
49         app.disabled("view cache");\r
50         app.set("view options", { layout: false })\r
51         app.set("views", __dirname + "/public");\r
52         app.set("view engine", "ejs");\r
53         app.use(express.bodyParser());\r
54         app.use(express.methodOverride());\r
55         app.use(express.cookieParser());\r
56         app.use(express.session({\r
57                 store:sessionStore,\r
58                 secret: "5514EA2B-C9B2-4D65-8D81-1F33A180A0C2",\r
59                 cookie: { httpOnly: false }\r
60         }));\r
61         app.use(app.router);\r
62         app.use(express.static(__dirname + "/public"));\r
63 });\r
64 \r
65 app.configure('development', function(){\r
66   app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); \r
67 });\r
68 \r
69 app.configure('production', function(){\r
70   app.use(express.errorHandler()); \r
71 });\r
72 \r
73 // Routes\r
74 app.get("/chat", function(req, res){\r
75         var auth_string = getRandomString($token_length);\r
76         req.session.items = {token:auth_string};\r
77 \r
78         var room_number = 0;\r
79         if(typeof(req.query.rno) != "undefined")\r
80                 room_number = req.query.rno;\r
81         res.render("chat",{rno:room_number,token:auth_string});\r
82 });\r
83 \r
84 app.all("/log/" + $logfile_pattern,express.basicAuth(function (user, pass) {\r
85         return user === $username && pass === $password;\r
86 }));\r
87 \r
88 app.get("/log/" + $logfile_pattern, function(req, res){\r
89         res.sendfile(__dirname + req.url);\r
90 });\r
91 \r
92 app.all("/admin",express.basicAuth(function (user, pass) {\r
93         return user === $username && pass === $password;\r
94 }));\r
95 \r
96 app.get("/admin", function(req, res){\r
97         renderAdmin(req,res);\r
98 });\r
99 \r
100 app.post("/admin",function(req,res){\r
101         if(req.session.items.token != req.body.token)\r
102         {\r
103                 res.send($invaild_token_message);\r
104                 return;\r
105         }\r
106         if(typeof(req.body.erase) != "undefined")\r
107         {\r
108                 removeLog(req.body.file,function(){\r
109                         renderAdmin(req,res);\r
110                 });\r
111         }\r
112         if(typeof(req.body.registor) != "undefined")\r
113         {\r
114                 updateIpBanList(req.body.newbanlist,function(){\r
115                         renderAdmin(req,res);\r
116                 });\r
117         }\r
118 });\r
119 \r
120 function renderAdmin(req,res)\r
121 {\r
122         var auth_string = getRandomString($token_length);\r
123         req.session.items = {token:auth_string};\r
124 \r
125         fs.readdir($log_directory,function(err,list){\r
126                 res.render("admin", {\r
127                         files: list,\r
128                         log_directory:$log_directory,\r
129                         ipbanlist:getTextFromIpBanlist(ipbanlist),\r
130                         token:auth_string\r
131                 });\r
132         });\r
133 }\r
134 \r
135 function getRandomString(length)\r
136 {\r
137         var RandomString = "";\r
138         var BaseString ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()=~|-^\@[;:],./\`{+*}<>?_";\r
139         for(var i=0; i<length; i++) {\r
140                 RandomString += BaseString.charAt( Math.floor( Math.random() * BaseString.length));\r
141         }\r
142         return RandomString\r
143 }\r
144 \r
145 function removeLog(files,callback)\r
146 {\r
147         async.map(files,\r
148         function(item,callback){\r
149                 fs.unlink($log_directory + "/" + item,callback);\r
150         },\r
151         function(err,results){\r
152                 if(typeof(callback) == "function")\r
153                         callback();\r
154         });\r
155 }\r
156 \r
157 function getTextFromIpBanlist(list)\r
158 {\r
159         var text = "";\r
160         for(var key in ipbanlist)\r
161         {\r
162                 if(ipbanlist[key] == "")\r
163                         text += key + "\r\n";\r
164                 else\r
165                         text += key + ":" + ipbanlist[key] + "\r\n";\r
166         }\r
167         return text;\r
168 }\r
169 \r
170 function updateIpBanList(text,callfunc)\r
171 {\r
172         async.waterfall([\r
173                 function(callback){\r
174                         fs.open($ip_ban_list_file_name,"w",callback);\r
175                 },\r
176                 function(fd,callback){\r
177                         var buf = new Buffer(text);\r
178                         fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
179                                 callback(null,fd);\r
180                         });\r
181                 },\r
182                 function(fd,callback){\r
183                         fs.close(fd,function(){\r
184                                 getIpBanList(callfunc);\r
185                         });\r
186                 }\r
187         ]);\r
188 }\r
189 \r
190 app.listen($port);\r
191 console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);\r
192 \r
193 /*\r
194  * \83T\81[\83o\81[\95\94\95ª\r
195  */\r
196 \r
197 var io = require("socket.io").listen(app);\r
198 io.configure('production', function(){\r
199   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
200   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
201   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
202 });\r
203 var clients = new Array();\r
204 \r
205 var ipbanlist = {};\r
206 \r
207 createLogDirectory();\r
208 \r
209 getIpBanList();\r
210 \r
211 for(var i = 0; i < $max_room_number; i++)\r
212 {\r
213         clients[i] =io\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.on("get pastLogList", function (msg) {\r
219                         ParseGetPastLogList(socket,msg);\r
220                 });\r
221                 socket.on("get pastLog", function (msg) {\r
222                         ParseGetPastLog(socket,msg);\r
223                 });\r
224                 socket.on("send msg", function (msg) {\r
225                         ParseSendMsg(socket,msg);\r
226                 });\r
227                 socket.on("disconnect", function (msg) {\r
228                         ParseDisconnect(socket,msg);\r
229                 });\r
230         });\r
231 }\r
232 \r
233 function createLogDirectory()\r
234 {\r
235         path.exists($log_directory,function(exists){\r
236                 if(exists == false)\r
237                         fs.mkdirSync($log_directory);\r
238         });\r
239 }\r
240 \r
241 function getIpBanList(callback)\r
242 {\r
243         ipbanlist = {};\r
244         path.exists($ip_ban_list_file_name,function(exists){\r
245                 if(exists == false)\r
246                 {\r
247                         if(typeof(callback) == "function")\r
248                                 callback();\r
249                         return;\r
250                 }\r
251                 var stream = fs.createReadStream($ip_ban_list_file_name);\r
252                 new lazy(stream)\r
253                         .lines\r
254                         .forEach(function(line){\r
255                                 var token = line.toString().split(":");\r
256                                 var ip = token[0].replace(/(\r|\n|\r\n)/gm, "");\r
257                                 if(token.length == 1)\r
258                                         ipbanlist[ip] = "";\r
259                                 else\r
260                                         ipbanlist[ip] = token[1];\r
261                         })\r
262                         .join(function(){\r
263                                 if(typeof(callback) == "function")\r
264                                         callback();\r
265                         });\r
266         });\r
267 }\r
268 \r
269 function ParseAuthorization(handshakeData, callback)\r
270 {\r
271         if(handshakeData.headers.cookie) {\r
272                 var cookie = handshakeData.headers.cookie;\r
273                 var sessionID = parseCookie(cookie)["connect.sid"];\r
274                 sessionStore.get(sessionID, function (err, session) {\r
275                         var result = null;\r
276                         if (err || ipbanlist[handshakeData.address.address] == "r")\r
277                                 result = "failed get from session store";\r
278                         else if(handshakeData.query.token != session.items.token)\r
279                                 result = "invaild token";\r
280                         sessionStore.destroy(sessionID);\r
281                         callback(result,result == null && !err);\r
282                 });\r
283         } else {\r
284                 return callback("failed get cookie", false);\r
285         }\r
286 }\r
287 \r
288 function ParseDisconnect(socket,msg)\r
289 {\r
290         console.log("disconnected");\r
291 }\r
292 \r
293 //socket \90Ú\91±\92\86\82Ì\83\\83P\83b\83g\r
294 //msg msg\83N\83\89\83X\r
295 function ParseSendMsg(socket,msg)\r
296 {\r
297         var ip = GetClientIPAdress(socket);\r
298 \r
299         if(ip in ipbanlist)\r
300         {\r
301                 socket.emit("error",$block_message);\r
302                 return;\r
303         }\r
304 \r
305         var date = new Date();\r
306         //var dateString = date.toFormat("YYYY/MM/DD HH24:MI:SS");\r
307 \r
308         var repacked_msg = CreateMessage(msg,date);\r
309 \r
310         socket.json.emit("req msg", repacked_msg);\r
311 \r
312         socket.json.broadcast.emit("req msg", repacked_msg);\r
313 \r
314         var rno = GetRoomNumberFromName(socket.namespace.name);\r
315         var path = $log_directory + "/" + util.format($log_file_name,rno);\r
316         var log = new ChatLog(path);\r
317         log.Save(repacked_msg,ip,rno);\r
318 }\r
319 \r
320 function GetNameFromRoomNumber(number)\r
321 {\r
322         return "/" + number;\r
323 }\r
324 \r
325 function GetRoomNumberFromName(name)\r
326 {\r
327         if(name.charAt(0) == "/")\r
328                 return parseInt(name.substr(1));\r
329         throw "GetRoomNumberFromName error";\r
330 }\r
331 \r
332 function ParseGetPastLogList(socket,msg)\r
333 {\r
334         var list = fs.readdir($log_directory,function(err,files){\r
335                 var text = "";\r
336                 var rno = GetRoomNumberFromName(socket.namespace.name);\r
337                 var pattern = $pastlogfile_pattern.replace("%d",rno);\r
338                 for(var i = 0; i < files.length; i++)\r
339                 {\r
340                         var logname = files[i];\r
341                         if(logname.match(pattern))\r
342                                 text += files[i] + "\n";\r
343                 }\r
344                 socket.emit("req pastloglist",text);\r
345         });\r
346 }\r
347 \r
348 function ParseGetPastLog(socket,file)\r
349 {\r
350         if(file == "")\r
351                 return;\r
352         var path = $log_directory + "/" + file;\r
353         var log = new ChatLog(path);\r
354         log.ToArray(function(array){\r
355                 socket.json.emit("req pastlog",array);\r
356         });\r
357 }\r
358 \r
359 function ChatLog(path)\r
360 {\r
361         this.ToArray = function(callback)\r
362         {\r
363                 var state = fs.stat(path,function(err,state){\r
364                         if(err)\r
365                                 return;\r
366                         var array = new Array();\r
367                         var stream = fs.createReadStream(path);\r
368                         new lazy(stream)\r
369                                 .lines\r
370                                 .forEach(function(line){\r
371                                         array.push(CreateMessageFromText(line.toString()));\r
372                                 })\r
373                                 .join(function(){\r
374                                         callback(array);\r
375                                 });\r
376                 });\r
377         }\r
378 \r
379         this.Save = function(msg,ip,rno){\r
380                 var text = GetTextFromMessage(msg,ip);\r
381 \r
382                 SplitLog(rno,function(){\r
383                         WritePastLog(path,text);\r
384                 });\r
385         };\r
386 \r
387         function GetTextFromMessage(msg,ip)\r
388         {\r
389                 var text = msg.name + "<>" +\r
390                                 msg.date + "<>" +\r
391                                 ip + "<>" +\r
392                                 msg.message +\r
393                                 "\n";\r
394                 return text;\r
395         }\r
396 \r
397         function SplitLog(rno,callback)\r
398         {\r
399                 var state = fs.stat(path,function(err,state){\r
400                         if(err && typeof(callback) == "function")\r
401                         {\r
402                                 callback();\r
403                                 return;\r
404                         }\r
405                         if(state.size > $spilt_size)\r
406                         {\r
407                                 var date = new Date();\r
408                                 var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds();\r
409 \r
410                                 var newpath = $log_directory + "/" +\r
411                                         util.format($splited_log_file_name,rno,dateString);\r
412                                 fs.rename(path,newpath,callback);\r
413                         }else{\r
414                                 if(typeof(callback) == "function")\r
415                                         callback();\r
416                         }\r
417                 });\r
418         }\r
419 \r
420         function WritePastLog(path,text)\r
421         {\r
422                 async.waterfall([\r
423                         function(callback){\r
424                                 fs.open(path,"a",callback);\r
425                         },\r
426                         function(fd,callback){\r
427                                 var buf = new Buffer(text);\r
428                                 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
429                                         callback(null,fd);\r
430                                 });\r
431                         },\r
432                         function(fd){\r
433                                 fs.close(fd);\r
434                         }\r
435                 ]);\r
436         }\r
437 }\r
438 \r
439 function GetClientIPAdress(socket)\r
440 {\r
441         return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;\r
442 }\r
443 \r
444 // Message \83N\83\89\83X\r
445 function CreateMessage(msg,date)\r
446 {\r
447         var result = {name:msg.name,\r
448                 date:date,\r
449                 message:msg.message};\r
450         return result;\r
451 }\r
452 function CreateMessageFromText(text)\r
453 {\r
454         var data = text.split("<>");\r
455         var msg = {name:data[0],\r
456                 date:data[1],\r
457                 message:data[3]};\r
458         return msg;\r
459 }\r