OSDN Git Service

ルーム設定更新時にゴミが入ってしまうバグを主性した
[webchat/WebChat.git] / chatServer.js
1 /*\r
2  * 設定\r
3  */\r
4 $max_room_number = 3;   //最大ルーム数\r
5 $spilt_size = 1024 * 512;       //分割するサイズ\r
6 $reset_password_diff = 1000 * 60 * 60;\r
7 $block_message = "メッセージの送信に失敗しました";       //ブロック時のメッセージ\r
8 $not_match_password = "パスワードが一致しませんでした";  //パスワードが一致しない場合に表示されるメッセージ\r
9 $password_setted_message = "パスワードを設定しました";      //パスワードが設定されたときに表示されるメッセージ\r
10 $password_resetted_message = "パスワードをリセットしました";      //パスワードが再設定されたときに表示されるメッセージ\r
11 $failed_set_password_message = "パスワードの設定に失敗しました"; //パスワードが再設定されたときに表示されるメッセージ\r
12 $ip_ban_list_file_name = "ipbanlist.txt";       //アクセスを禁止するIPが記録されているファイル\r
13 $port = process.env.port || 3000;       //ポート\r
14 $username = "admin";    //管理者用のページにアクセスできるユーザ名\r
15 $password = "admin";    //管理者用のページにアクセスするのに必要なパスワード\r
16 $token_length = 32;     //トークンの長さ\r
17 $redisHost = "localhost";       //redisサーバのアドレス\r
18 $redisPort = 6379;      //redisサーバのポート\r
19 $redisPassword = "";    //redisサーバのパスワード\r
20 $system_name = "system";        //システム発言を表す名前\r
21 $log_directory = "log"; //ログファイルを置くフォルダー\r
22 $log_file_name = "logfile%d.txt";       //ログファイル名(%dはそのままにしておくこと)\r
23 $splited_log_file_name = "logfile%d_%s.txt"     //分割後のファイル名(%dと%sはそのままにしておくこと)\r
24 $pastlogfile_pattern = "logfile%d(_+.*)?\.txt"; //過去ログと判定する正規表現\r
25 $logfile_pattern = "logfile[0-9]+(_*.*)?\.txt"  //過去ログと判定する正規表現\r
26 //パスワードを自由に設定できる部屋のリスト\r
27 //      rno     設定したい部屋番号\r
28 //      password        パスワードを設定する。nullにすると利用者がパスワードの設定をすることができる\r
29 //記述例:\r
30 //      new RoomInfomationCollection(\r
31 //              {rno:"1",password:"test"},\r
32 //              {rno:"2",password:null}\r
33 //      )\r
34 $rooms = new RoomInfomationCollection(\r
35         {rno:"1",password:"test"},\r
36         {rno:"2",password:null}\r
37 );\r
38 \r
39 /**\r
40  * Module dependencies.\r
41  */\r
42 \r
43 // Server\r
44 var express = require('express');\r
45 \r
46 var app = module.exports = express.createServer();\r
47 \r
48 var util = require("util");\r
49 \r
50 var lazy = require("lazy");\r
51 \r
52 var fs = require("fs");\r
53 \r
54 var parseCookie = require("connect").utils.parseCookie;\r
55 \r
56 var RedisStore = require("connect-redis")(express);\r
57 var sessionStore = new RedisStore({host:$redisHost,port:$redisPort,pass:$redisPassword});\r
58 \r
59 var async = require("async");\r
60 \r
61 var path = require("path");\r
62 \r
63 // Configuration\r
64 \r
65 app.configure(function(){\r
66         app.disabled("view cache");\r
67         app.set("view options", { layout: false })\r
68         app.set("views", __dirname + "/public");\r
69         app.set("view engine", "ejs");\r
70         app.use(express.bodyParser());\r
71         app.use(express.methodOverride());\r
72         app.use(express.cookieParser());\r
73         app.use(express.session({\r
74                 store:sessionStore,\r
75                 secret: "5514EA2B-C9B2-4D65-8D81-1F33A180A0C2",\r
76                 cookie: { httpOnly: false }\r
77         }));\r
78         app.use(app.router);\r
79         app.use(express.static(__dirname + "/public"));\r
80 });\r
81 \r
82 app.configure('development', function(){\r
83   app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); \r
84 });\r
85 \r
86 app.configure('production', function(){\r
87   app.use(express.errorHandler()); \r
88 });\r
89 \r
90 // Routes\r
91 app.get("/chat", function(req, res){\r
92         var auth_string = getRandomString($token_length);\r
93         req.session.items = {token:auth_string};\r
94 \r
95         var room_number = 0;\r
96         if(typeof(req.query.rno) != "undefined")\r
97                 room_number = req.query.rno;\r
98         res.render("chat",{rno:room_number,token:auth_string});\r
99 });\r
100 \r
101 app.all("/log/" + $logfile_pattern,express.basicAuth(function (user, pass) {\r
102         return user === $username && pass === $password;\r
103 }));\r
104 \r
105 app.get("/log/" + $logfile_pattern, function(req, res){\r
106         res.sendfile(__dirname + req.url);\r
107 });\r
108 \r
109 app.all("/admin",express.basicAuth(function (user, pass) {\r
110         return user === $username && pass === $password;\r
111 }));\r
112 \r
113 app.get("/admin", function(req, res){\r
114         renderAdmin(req,res);\r
115 });\r
116 \r
117 app.post("/admin",function(req,res){\r
118         if(req.session.items.token != req.body.token)\r
119         {\r
120                 res.send($invaild_token_message);\r
121                 return;\r
122         }\r
123         if(typeof(req.body.erase) != "undefined")\r
124         {\r
125                 removeLog(req.body.file,function(){\r
126                         renderAdmin(req,res);\r
127                 });\r
128         }\r
129         if(typeof(req.body.registor) != "undefined")\r
130         {\r
131                 ipbanlist.Update(req.body.newbanlist,function(){\r
132                         renderAdmin(req,res);\r
133                 });\r
134         }\r
135         if(typeof(req.body.updateroom) != "undefined")\r
136         {\r
137                 CreateRoomsFromString(req.body.newroomlist);\r
138                 renderAdmin(req,res);\r
139         }\r
140 });\r
141 \r
142 function renderAdmin(req,res)\r
143 {\r
144         var auth_string = getRandomString($token_length);\r
145         req.session.items = {token:auth_string};\r
146         var iplist = ipbanlist.GetText();\r
147 \r
148         fs.readdir($log_directory,function(err,list){\r
149                 res.render("admin", {\r
150                         files: list,\r
151                         log_directory:$log_directory,\r
152                         ipbanlist:iplist,\r
153                         token:auth_string,\r
154                         roomlist:$rooms.GetString()\r
155                 });\r
156         });\r
157 }\r
158 \r
159 function getRandomString(length)\r
160 {\r
161         var RandomString = "";\r
162         var BaseString ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()=~|-^\@[;:],./\`{+*}<>?_";\r
163         for(var i=0; i<length; i++) {\r
164                 RandomString += BaseString.charAt( Math.floor( Math.random() * BaseString.length));\r
165         }\r
166         return RandomString\r
167 }\r
168 \r
169 function removeLog(files,callback)\r
170 {\r
171         async.map(files,\r
172         function(item,callback){\r
173                 fs.unlink($log_directory + "/" + item,callback);\r
174         },\r
175         function(err,results){\r
176                 if(typeof(callback) == "function")\r
177                         callback();\r
178         });\r
179 }\r
180 \r
181 app.listen($port);\r
182 console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);\r
183 \r
184 /*\r
185  * サーバー部分\r
186  */\r
187 \r
188 var io = require("socket.io").listen(app);\r
189 io.configure('production', function(){\r
190   io.enable('browser client minification');  // minified されたクライアントファイルを送信する\r
191   io.enable('browser client etag');          // バージョンによって etag によるキャッシングを有効にする\r
192   io.set('log level', 1);                    // ログレベルを設定(デフォルトより下げている)\r
193 });\r
194 var clients = new Array();\r
195 \r
196 var ipbanlist = new IpBanCollecion();\r
197 \r
198 createLogDirectory();\r
199 \r
200 for(var i = 0; i < $max_room_number; i++)\r
201 {\r
202         clients[i] =io\r
203         .of(GetNameFromRoomNumber(i))\r
204         .authorization(ParseAuthorization)\r
205         .on("connection", function (socket) {\r
206                 console.log("connected from %s",GetClientIPAdress(socket));\r
207 \r
208                 var rno = GetRoomNumberFromName(socket.namespace.name);\r
209                 var roomconfig = {};\r
210                 if($rooms.IsFixedPassword(rno))\r
211                         roomconfig.type = 2;\r
212                 else if($rooms.IsContains(rno))\r
213                         roomconfig.type = 1;\r
214                 else\r
215                         roomconfig.type = 0;\r
216                 roomconfig.IsOwned = !$rooms.IsFirstAuth(rno);\r
217                 socket.json.emit("send roominfo",roomconfig);\r
218 \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("set password",function(msg){\r
232                         ParseSetPassword(socket,msg);\r
233                 });\r
234                 socket.on("send msg", function (msg) {\r
235                         ParseSendMsg(socket,msg);\r
236                 });\r
237                 socket.on("disconnect", function (msg) {\r
238                         ParseDisconnect(socket,msg);\r
239                 });\r
240         });\r
241 }\r
242 \r
243 function createLogDirectory()\r
244 {\r
245         path.exists($log_directory,function(exists){\r
246                 if(exists == false)\r
247                         fs.mkdirSync($log_directory);\r
248         });\r
249 }\r
250 \r
251 function ParseAuthorization(handshakeData, callback)\r
252 {\r
253         if(handshakeData.headers.cookie) {\r
254                 var cookie = handshakeData.headers.cookie;\r
255                 var sessionID = parseCookie(cookie)["connect.sid"];\r
256                 sessionStore.get(sessionID, function (err, session) {\r
257                         var result = null;\r
258                         if (err || ipbanlist.IsBaned(handshakeData.address.address))\r
259                                 result = "failed get from session store";\r
260                         else if(handshakeData.query.token != session.items.token)\r
261                                 result = "invaild token";\r
262                         sessionStore.destroy(sessionID);\r
263                         callback(result,result == null && !err);\r
264                 });\r
265         } else {\r
266                 return callback("failed get cookie", false);\r
267         }\r
268 }\r
269 \r
270 function ParseDisconnect(socket,msg)\r
271 {\r
272         console.log("disconnected");\r
273 }\r
274 \r
275 function ParseSetPassword(socket,msg)\r
276 {\r
277         var rno = GetRoomNumberFromName(socket.namespace.name);\r
278         var newMeg = {\r
279                 name:$system_name,\r
280                 message:null,\r
281         };\r
282         if($rooms.SetPassword(rno,msg.owner,msg.password))\r
283                 newMeg.message = $password_setted_message;\r
284         else\r
285                 newMeg.message = $failed_set_password_message;\r
286         ParseSendMsg(socket,newMeg);\r
287 }\r
288 \r
289 function ParseJoin(socket,msg)\r
290 {\r
291         var ip = GetClientIPAdress(socket);\r
292 \r
293         if(ipbanlist.IsBlockedToWrite(ip))\r
294         {\r
295                 socket.emit("error",$block_message);\r
296                 return;\r
297         }\r
298 \r
299         var rno = GetRoomNumberFromName(socket.namespace.name);\r
300         if($rooms.IsContains(rno))\r
301         {\r
302                 if($rooms.IsTimeout(rno) ||\r
303                         $rooms.IsFirstAuth(rno))\r
304                 {\r
305                         $rooms.Reset(rno,msg.name);\r
306                         ParseGetPastLog(socket,util.format($log_file_name,rno));\r
307                 }\r
308                 else if($rooms.Auth(rno,msg.name,msg.password))\r
309                 {\r
310                         ParseGetPastLog(socket,util.format($log_file_name,rno));\r
311                 }\r
312                 else\r
313                 {\r
314                         socket.emit("error",$not_match_password);\r
315                         return;\r
316                 }\r
317         }\r
318 \r
319         var newMeg = {\r
320                 name:$system_name,\r
321                 message:util.format("/enteredby %s %s %s",msg.name,msg.color,msg.mailto),\r
322         };\r
323         ParseSendMsg(socket,newMeg);\r
324 }\r
325 \r
326 function ParseQuit(socket,msg)\r
327 {\r
328         var ip = GetClientIPAdress(socket);\r
329 \r
330         if(ipbanlist.IsBlockedToWrite(ip))\r
331         {\r
332                 socket.emit("error",$block_message);\r
333                 return;\r
334         }\r
335 \r
336         var rno = GetRoomNumberFromName(socket.namespace.name);\r
337 \r
338         var newMeg = {\r
339                 name:$system_name,\r
340                 message:$password_resetted_message,\r
341         };\r
342         if($rooms.IsContains(rno))\r
343         {\r
344                 if($rooms.IsOwner(rno,msg.name))\r
345                 {\r
346                         $rooms.Reset(rno,null);\r
347                         ParseSendMsg(socket,newMeg);\r
348                 }\r
349                 if(!$rooms.IsFirstAuth(rno) &&\r
350                         !$rooms.IsAuthed(rno,msg.name))\r
351                         return;\r
352                 else\r
353                         $rooms.RemoveAuth(rno,msg.name);\r
354         }\r
355 \r
356         newMeg.message = util.format("/quitedby %s",msg.name);\r
357         ParseSendMsg(socket,newMeg);\r
358 }\r
359 \r
360 //socket 接続中のソケット\r
361 //msg msgクラス\r
362 function ParseSendMsg(socket,msg)\r
363 {\r
364         var ip = GetClientIPAdress(socket);\r
365 \r
366         if(ip in ipbanlist)\r
367         {\r
368                 socket.emit("error",$block_message);\r
369                 return;\r
370         }\r
371 \r
372         var rno = GetRoomNumberFromName(socket.namespace.name);\r
373 \r
374         if(msg.name != $system_name && \r
375                 $rooms.IsContains(rno) &&\r
376                 !$rooms.IsAuthed(rno,msg.name) &&\r
377                 !$rooms.IsOwner(rno,msg.name))\r
378         {\r
379                 return;\r
380         }\r
381 \r
382         var date = new Date();\r
383 \r
384         var repacked_msg = CreateMessage(msg.name,date,msg.message);\r
385 \r
386         socket.json.emit("req msg", repacked_msg);\r
387 \r
388         socket.json.broadcast.emit("req msg", repacked_msg);\r
389 \r
390         var path = $log_directory + "/" + util.format($log_file_name,rno);\r
391         var log = new ChatLog(path);\r
392         log.Save(repacked_msg,ip,rno);\r
393 }\r
394 \r
395 function GetNameFromRoomNumber(number)\r
396 {\r
397         return "/" + number;\r
398 }\r
399 \r
400 function GetRoomNumberFromName(name)\r
401 {\r
402         if(name.charAt(0) == "/")\r
403                 return parseInt(name.substr(1));\r
404         throw "GetRoomNumberFromName error";\r
405 }\r
406 \r
407 function ParseGetPastLogList(socket,msg)\r
408 {\r
409         var list = fs.readdir($log_directory,function(err,files){\r
410                 var text = "";\r
411                 var rno = GetRoomNumberFromName(socket.namespace.name);\r
412                 var pattern = $pastlogfile_pattern.replace("%d",rno);\r
413                 for(var i = 0; i < files.length; i++)\r
414                 {\r
415                         var logname = files[i];\r
416                         if(logname.match(pattern))\r
417                                 text += files[i] + "\n";\r
418                 }\r
419                 socket.emit("req pastloglist",text);\r
420         });\r
421 }\r
422 \r
423 function ParseGetPastLog(socket,file)\r
424 {\r
425         if(file == "")\r
426                 return;\r
427         var path = $log_directory + "/" + file;\r
428         var log = new ChatLog(path);\r
429         log.ToArray(function(array){\r
430                 socket.json.emit("req pastlog",array);\r
431         });\r
432 }\r
433 \r
434 function ChatLog(path)\r
435 {\r
436         this.ToArray = function(callback)\r
437         {\r
438                 var state = fs.stat(path,function(err,state){\r
439                         if(err)\r
440                                 return;\r
441                         var array = new Array();\r
442                         var stream = fs.createReadStream(path);\r
443                         new lazy(stream)\r
444                                 .lines\r
445                                 .forEach(function(line){\r
446                                         array.push(CreateMessageFromText(line.toString()));\r
447                                 })\r
448                                 .join(function(){\r
449                                         callback(array);\r
450                                 });\r
451                 });\r
452         }\r
453 \r
454         this.Save = function(msg,ip,rno){\r
455                 var text = GetTextFromMessage(msg,ip);\r
456 \r
457                 SplitLog(rno,function(){\r
458                         WritePastLog(path,text);\r
459                 });\r
460         };\r
461 \r
462         function GetTextFromMessage(msg,ip)\r
463         {\r
464                 var text = msg.name + "<>" +\r
465                                 msg.date + "<>" +\r
466                                 ip + "<>" +\r
467                                 msg.message +\r
468                                 "\n";\r
469                 return text;\r
470         }\r
471 \r
472         function SplitLog(rno,callback)\r
473         {\r
474                 var state = fs.stat(path,function(err,state){\r
475                         if(err && typeof(callback) == "function")\r
476                         {\r
477                                 callback();\r
478                                 return;\r
479                         }\r
480                         if(state.size > $spilt_size)\r
481                         {\r
482                                 var date = new Date();\r
483                                 var dateString = ""+date.getFullYear()+date.getMonth()+date.getDate()+date.getHours()+date.getMinutes()+date.getSeconds();\r
484 \r
485                                 var newpath = $log_directory + "/" +\r
486                                         util.format($splited_log_file_name,rno,dateString);\r
487                                 fs.rename(path,newpath,callback);\r
488                         }else{\r
489                                 if(typeof(callback) == "function")\r
490                                         callback();\r
491                         }\r
492                 });\r
493         }\r
494 \r
495         function WritePastLog(path,text)\r
496         {\r
497                 async.waterfall([\r
498                         function(callback){\r
499                                 fs.open(path,"a",callback);\r
500                         },\r
501                         function(fd,callback){\r
502                                 var buf = new Buffer(text);\r
503                                 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
504                                         callback(null,fd);\r
505                                 });\r
506                         },\r
507                         function(fd){\r
508                                 fs.close(fd);\r
509                         }\r
510                 ]);\r
511         }\r
512 }\r
513 \r
514 function GetClientIPAdress(socket)\r
515 {\r
516         return socket.handshake.headers["x-forwarded-for"] || socket.handshake.address.address;\r
517 }\r
518 \r
519 // Message クラス\r
520 function CreateMessage(name,date,message)\r
521 {\r
522         var result = {name:name,\r
523                 date:date,\r
524                 message:message};\r
525         return result;\r
526 }\r
527 function CreateMessageFromText(text)\r
528 {\r
529         var data = text.split("<>");\r
530         var msg = {name:data[0],\r
531                 date:data[1],\r
532                 message:data[3]};\r
533         return msg;\r
534 }\r
535 \r
536 //RoomInfomationCollecionクラス\r
537 function RoomInfomationCollection()\r
538 {\r
539         var collection = {};\r
540         this.Clear = function(){\r
541                 collection = {};\r
542         };\r
543         this.Add = function(rno,pass){\r
544                 collection[rno] = {time : null,\r
545                         password : pass,\r
546                         owner : null,\r
547                         authed_list : {}};\r
548                 if(pass != null)\r
549                         collection[rno].owner = $system_name;\r
550         };\r
551         this.Reset = function(rno,owner){\r
552                 var date = new Date();\r
553                 var time = date.getTime();\r
554                 collection[rno].password = null;\r
555                 collection[rno].authed_list = {};\r
556                 collection[rno].owner = owner;\r
557                 collection[rno].time = time;\r
558                 console.log(util.format("password is reseted in %s",rno));\r
559         };\r
560         this.IsContains = function(rno){\r
561                 return rno in collection;\r
562         };\r
563         this.IsFirstAuth = function(rno){\r
564                 if(!this.IsContains(rno) || typeof(collection[rno].owner) == "undefined")\r
565                         return false;\r
566                 return collection[rno].owner == null;\r
567         }\r
568         this.IsAuthed = function(rno,name){\r
569                 if(!this.IsContains(rno))\r
570                         return false;\r
571                 return name == collection[rno].owner ||\r
572                         name in collection[rno].authed_list;\r
573         };\r
574         this.IsFixedPassword = function(rno){\r
575                 if(!this.IsContains(rno))\r
576                         return false;\r
577                 return collection[rno].owner == $system_name;\r
578         };\r
579         this.IsOwner = function(rno,name){\r
580                 return this.IsContains(rno) &&\r
581                         typeof(collection[rno].owner) != "undefined" &&\r
582                         collection[rno].owner == name;\r
583         }\r
584         this.IsTimeout = function(rno){\r
585                 var date = new Date();\r
586                 var time = date.getTime();\r
587                 return this.IsContains(rno) &&\r
588                         !this.IsFixedPassword(rno) &&\r
589                         (typeof(collection[rno].time) != "undefined" &&\r
590                         time - collection[rno].time >= $reset_password_diff);\r
591         };\r
592         this.RemoveAuth = function(rno,name)\r
593         {\r
594                 delete collection[rno].authed_list[name];\r
595         }\r
596         this.Auth = function(rno,name,password){\r
597                 if(typeof(collection[rno].password) != "undefined" &&\r
598                         collection[rno].password != password)\r
599                         return false;\r
600                 var date = new Date();\r
601                 var time = date.getTime();\r
602                 collection[rno].time = time;\r
603                 collection[rno].authed_list[name] = "";\r
604                 return true;\r
605         }\r
606         this.SetPassword = function(rno,owner,password){\r
607                 if(this.IsContains(rno) && \r
608                         owner == collection[rno].owner &&\r
609                         !this.IsFixedPassword(rno))\r
610                 {\r
611                         var date = new Date();\r
612                         collection[rno].time = date.getTime();\r
613                         collection[rno].password = password;\r
614                         \r
615                         console.log(util.format("password is seted to %s in %s",password,rno));\r
616                         return true;\r
617                 }\r
618                 return false;\r
619         };\r
620         this.GetString = function(){\r
621                 var retval = "";\r
622                 for(var rno in collection)\r
623                 {\r
624                         var pass = collection[rno].password;\r
625                         if(pass == null)\r
626                                 pass = "";\r
627                         retval += rno + ":" + pass + "\r\n";\r
628                 }\r
629                 return retval;\r
630         };\r
631         this.GetKeys = function(){\r
632                 var retval = {};\r
633                 for(var rno in collection)\r
634                 {\r
635                         retval[rno] = {};\r
636                 }\r
637                 return retval;\r
638         }\r
639 \r
640         for(var i=0; i<arguments.length; i++)\r
641         {\r
642                 this.Add(arguments[i].rno,arguments[i].password);\r
643         }       \r
644 }\r
645 \r
646 function CreateRoomsFromString(str)\r
647 {\r
648         $rooms.Clear();\r
649         var lines = str.split("\r\n");\r
650         for(var i in lines)\r
651         {\r
652                 if(lines[i] == "")\r
653                         continue;\r
654                 var token = lines[i].split(":");\r
655                 if(token.length == 1)\r
656                 {\r
657                         $rooms.Add(token[0],null);\r
658                 }\r
659                 else if(token.length == 2)\r
660                 {\r
661                         var rno = token[0];\r
662                         var pass = token[1];\r
663                         if(pass == "")\r
664                                 pass = null;\r
665                         $rooms.Add(rno, pass);\r
666                 }\r
667         }\r
668 }\r
669 \r
670 //IPBANクラス\r
671 function IpBanCollecion()\r
672 {\r
673         var collection = {};\r
674         this.IsBaned = function(ip){\r
675                 return collection[ip] == "r";\r
676         }\r
677         this.IsBlockedToWrite = function(ip){\r
678                 return ip in collection;\r
679         }\r
680         this.GetText = function(){\r
681                 var text = "";\r
682                 for(var key in collection)\r
683                 {\r
684                         if(collection[key] == "")\r
685                                 text += key + "\r\n";\r
686                         else\r
687                                 text += key + ":" + collection[key] + "\r\n";\r
688                 }\r
689                 return text;\r
690         }\r
691         this.Update = function(text,callfunc){\r
692                 async.waterfall([\r
693                         function(callback){\r
694                                 fs.open($ip_ban_list_file_name,"w",callback);\r
695                         },\r
696                         function(fd,callback){\r
697                                 var buf = new Buffer(text);\r
698                                 fs.write(fd,buf,0,Buffer.byteLength(text),null,function(){\r
699                                         callback(null,fd);\r
700                                 });\r
701                         },\r
702                         function(fd,callback){\r
703                                 fs.close(fd,function(){\r
704                                         GetIpBanList(callfunc);\r
705                                 });\r
706                         }\r
707                 ]);\r
708         }\r
709         function GetIpBanList(callback)\r
710         {\r
711                 collection = {};\r
712                 path.exists($ip_ban_list_file_name,function(exists){\r
713                         if(exists == false)\r
714                         {\r
715                                 if(typeof(callback) == "function")\r
716                                         callback();\r
717                                 return;\r
718                         }\r
719                         var stream = fs.createReadStream($ip_ban_list_file_name);\r
720                         new lazy(stream)\r
721                                 .lines\r
722                                 .forEach(function(line){\r
723                                         var token = line.toString().replace(/(\r|\n|\r\n)/gm, "").split(":");\r
724                                         var ip = token[0];\r
725                                         if(token.length == 1)\r
726                                                 collection[ip] = "";\r
727                                         else\r
728                                                 collection[ip] = token[1];\r
729                                 })\r
730                                 .join(function(){\r
731                                         if(typeof(callback) == "function")\r
732                                                 callback();\r
733                                 });\r
734                 });\r
735         }\r
736         GetIpBanList();\r
737 }\r
738 \r