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 = 16;     //\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                         if (err || ipbanlist[handshakeData.address.address] == "r") {\r
276                                 callback("failed get from session store", false);\r
277                         } else {\r
278                                 var result = null;\r
279                                 if(handshakeData.query.token != session.items.token)\r
280                                         return callback("invaild token", false);\r
281                                 sessionStore.destroy(sessionID);\r
282                                 return callback(result, true);\r
283                         }\r
284                 });\r
285         } else {\r
286                 return callback("failed get cookie", false);\r
287         }\r
288 }\r
289 \r
290 function ParseDisconnect(socket,msg)\r
291 {\r
292         console.log("disconnected");\r
293 }\r
294 \r
295 //socket \90Ú\91±\92\86\82Ì\83\\83P\83b\83g\r
296 //msg msg\83N\83\89\83X\r
297 function ParseSendMsg(socket,msg)\r
298 {\r
299         var ip = GetClientIPAdress(socket);\r
300 \r
301         if(ip in ipbanlist)\r
302         {\r
303                 socket.emit("error",$block_message);\r
304                 return;\r
305         }\r
306 \r
307         var date = new Date();\r
308         //var dateString = date.toFormat("YYYY/MM/DD HH24:MI:SS");\r
309 \r
310         var repacked_msg = CreateMessage(msg,date);\r
311 \r
312         socket.json.emit("req msg", repacked_msg);\r
313 \r
314         socket.json.broadcast.emit("req msg", repacked_msg);\r
315 \r
316         var rno = GetRoomNumberFromName(socket.namespace.name);\r
317         var path = $log_directory + "/" + util.format($log_file_name,rno);\r
318         var log = new ChatLog(path);\r
319         log.Save(repacked_msg,ip);\r
320 }\r
321 \r
322 function GetNameFromRoomNumber(number)\r
323 {\r
324         return "/" + number;\r
325 }\r
326 \r
327 function GetRoomNumberFromName(name)\r
328 {\r
329         if(name.charAt(0) == "/")\r
330                 return parseInt(name.substr(1));\r
331         throw "GetRoomNumberFromName error";\r
332 }\r
333 \r
334 function ParseGetPastLogList(socket,msg)\r
335 {\r
336         var list = fs.readdir($log_directory,function(err,files){\r
337                 var text = "";\r
338                 var rno = GetRoomNumberFromName(socket.namespace.name);\r
339                 var pattern = $pastlogfile_pattern.replace("%d",rno);\r
340                 for(var i = 0; i < files.length; i++)\r
341                 {\r
342                         var logname = files[i];\r
343                         if(logname.match(pattern))\r
344                                 text += files[i] + "\n";\r
345                 }\r
346                 socket.emit("req pastloglist",text);\r
347         });\r
348 }\r
349 \r
350 function ParseGetPastLog(socket,file)\r
351 {\r
352         if(file == "")\r
353                 return;\r
354         var path = $log_directory + "/" + file;\r
355         var log = new ChatLog(path);\r
356         log.ForEach(function(line){\r
357                 socket.json.emit("req pastlog",CreateMessageFromText(line.toString()));\r
358         });\r
359 }\r
360 \r
361 function ChatLog(path)\r
362 {\r
363         this.ForEach = function(callback)\r
364         {\r
365                 var state = fs.stat(path,function(err,state){\r
366                         if(err)\r
367                                 return;\r
368                         var stream = fs.createReadStream(path);\r
369                         new lazy(stream)\r
370                                 .lines\r
371                                 .forEach(callback);\r
372                 });\r
373         }\r
374 \r
375         this.Save = function(msg,ip){\r
376                 var text = GetTextFromMessage(msg,ip);\r
377 \r
378                 SplitLog(path,function(){\r
379                         WritePastLog(path,text);\r
380                 });\r
381         };\r
382 \r
383         function GetTextFromMessage(msg,ip)\r
384         {\r
385                 var text = msg.name + "<>" +\r
386                                 msg.date + "<>" +\r
387                                 ip + "<>" +\r
388                                 msg.message +\r
389                                 "\n";\r
390                 return text;\r
391         }\r
392 \r
393         function SplitLog(path,callback)\r
394         {\r
395                 var state = fs.stat(path,function(err,state){\r
396                         if(err && typeof(callback) == "function")\r
397                         {\r
398                                 callback();\r
399                                 return;\r
400                         }\r
401                         if(state.size > $spilt_size)\r
402                         {\r
403                                 var date = new Date();\r
404 \r
405                                 var newpath = $log_directory + "/" +\r
406                                         util.format($splited_log_file_name,rno,date.toFormat("YYYYMMDDHH24MISS"));\r
407                                 fs.rename(path,newpath,callback);\r
408                         }else{\r
409                                 if(typeof(callback) == "function")\r
410                                         callback();\r
411                         }\r
412                 });\r
413         }\r
414 \r
415         function WritePastLog(path,text)\r
416         {\r
417                 async.waterfall([\r
418                         function(callback){\r
419                                 fs.open(path,"a",callback);\r
420                         },\r
421                         function(fd,callback){\r
422                                 var buf = new Buffer(text);\r
423                                 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
424                                         callback(null,fd);\r
425                                 });\r
426                         },\r
427                         function(fd){\r
428                                 fs.close(fd);\r
429                         }\r
430                 ]);\r
431         }\r
432 }\r
433 \r
434 function GetClientIPAdress(socket)\r
435 {\r
436         return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;\r
437 }\r
438 \r
439 // Message \83N\83\89\83X\r
440 function CreateMessage(msg,date)\r
441 {\r
442         var result = {name:msg.name,\r
443                 date:date,\r
444                 message:msg.message};\r
445         return result;\r
446 }\r
447 function CreateMessageFromText(text)\r
448 {\r
449         var data = text.split("<>");\r
450         var msg = {name:data[0],\r
451                 date:data[1],\r
452                 message:data[3]};\r
453         return msg;\r
454 }\r