OmnomIRC.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!node
  2. process.chdir(__dirname);
  3. var fs = require('fs'),
  4. url = require('url'),
  5. path = require('path'),
  6. vm = require('vm'),
  7. toobusy = function(){return false;},//require('toobusy'),
  8. cluster = require('cluster'),
  9. options = global.options = (function(){
  10. var defaults = {
  11. port: 80,
  12. loglevel: 3,
  13. redis: {
  14. port: 6379,
  15. host: 'localhost'
  16. },
  17. debug: false
  18. },
  19. i,
  20. options;
  21. try{
  22. options = JSON.parse(fs.readFileSync('./options.json'));
  23. for(var i in options){
  24. defaults[i] = options[i];
  25. }
  26. }catch(e){
  27. console.warn('Using default settings. Please create options.json');
  28. }
  29. options = {};
  30. for(i in defaults){
  31. Object.defineProperty(options,i,{
  32. value: defaults[i],
  33. enumerable: true,
  34. writable: false
  35. });
  36. }
  37. return options;
  38. })();
  39. if(typeof fs.existsSync == 'undefined') fs.existsSync = path.existsSync; // legacy support
  40. if(cluster.isMaster){
  41. for(var i=0;i<require('os').cpus().length;i++){
  42. cluster.fork();
  43. }
  44. cluster.on('exit', function(worker, code, signal) {
  45. console.log('worker ' + worker.process.pid + ' died');
  46. });
  47. if(options.debug){
  48. require('repl').start({
  49. prompt: '> ',
  50. useGlobal: true
  51. }).on('exit',function(){
  52. process.exit();
  53. });
  54. }
  55. }else{
  56. var RedisStore = require('socket.io/lib/stores/redis'),
  57. redis = require('socket.io/node_modules/redis'),
  58. pub = redis.createClient(options.redis.port,options.redis.host),
  59. sub = redis.createClient(options.redis.port,options.redis.host),
  60. client = redis.createClient(options.redis.port,options.redis.host),
  61. mimeTypes = {
  62. 'html': 'text/html',
  63. 'js': 'text/javascript',
  64. 'css': 'text/css',
  65. 'png': 'image/png',
  66. 'jpg': 'image/jpeg'
  67. },
  68. app = require('http').createServer(function(req,res){
  69. if(toobusy()){
  70. res.writeHead(503,{
  71. 'Content-type': 'text/plain'
  72. });
  73. res.write('503 Server busy.\n');
  74. res.end();
  75. return;
  76. }
  77. req.addListener('end',function(){
  78. logger.debug('served static content for '+req.url);
  79. var uri = url.parse(req.url).pathname,
  80. serveFile = function(filename,req,res){
  81. try{
  82. stats = fs.lstatSync(filename);
  83. }catch(e){
  84. res.writeHead(404,{
  85. 'Content-type': 'text/plain'
  86. });
  87. res.write('404 Not Found.\n');
  88. res.end();
  89. return;
  90. }
  91. if(stats.isFile()){
  92. var fileStream,
  93. mimetype = mimeTypes[path.extname(filename).split('.')[1]];
  94. res.writeHead(200,{
  95. 'Content-Type': mimetype
  96. });
  97. fileStream = fs.createReadStream(filename);
  98. fileStream.pipe(res);
  99. }else if(stats.isDirectory()){
  100. if(fs.existsSync(path.join(filename,'index.html'))){
  101. serveFile(path.join(filename,'index.html'),req,res);
  102. }else if(fs.existsSync(path.join(filename,'index.htm'))){
  103. serveFile(path.join(filename,'index.htm'),req,res);
  104. }else if(fs.existsSync(path.join(filename,'index.txt'))){
  105. serveFile(path.join(filename,'index.txt'),req,res);
  106. }else{
  107. res.writeHead(200,{
  108. 'Content-Type': 'text/plain'
  109. });
  110. res.write('Index of '+url+'\n');
  111. res.write('TODO, show index');
  112. res.end();
  113. }
  114. }else{
  115. res.writeHead(500,{
  116. 'Content-Type': 'text/plain'
  117. });
  118. res.write('500 Internal server error\n');
  119. res.end();
  120. }
  121. },
  122. filepath = unescape(uri);
  123. if(filepath.substr(0,5) == '/api/'){
  124. filepath = path.join('./api/',filepath.substr(5));
  125. logger.debug('Attempting to run api script '+filepath);
  126. if(fs.existsSync(filepath)){
  127. fs.readFile(filepath,function(e,data){
  128. if(e){
  129. logger.error(e);
  130. res.end('null;');
  131. }else{
  132. var output = '',
  133. sandbox = {
  134. log: function(text){
  135. output += text;
  136. },
  137. error: function(msg){
  138. logger.error(msg);
  139. },
  140. info: function(msg){
  141. logger.info(msg);
  142. },
  143. debug: function(msg){
  144. logger.debug(msg);
  145. },
  146. head: {
  147. 'Content-Type': 'text/javascript'
  148. },
  149. returnCode: 200,
  150. vm: vm,
  151. fs: fs
  152. };
  153. vm.runInNewContext(data,sandbox,filepath);
  154. res.writeHead(sandbox.returnCode,sandbox.head);
  155. res.end(output);
  156. }
  157. });
  158. }else{
  159. res.writeHead(404,{
  160. 'Content-Type': 'text/javascript'
  161. });
  162. res.end('null;');
  163. }
  164. }else{
  165. serveFile(path.join('./www/',filepath),req,res);
  166. }
  167. }).resume();
  168. }).listen(options.port),
  169. io = require('socket.io').listen(app)
  170. logger = io.log;
  171. io.set('log level',options.loglevel);
  172. if(typeof options.redis.password != 'undefined'){
  173. var eh = function(e){
  174. throw e;
  175. };
  176. pub.auth(options.redis.ppassword,eh);
  177. sub.auth(options.redis.ppassword,eh);
  178. client.auth(options.redis.ppassword,eh);
  179. }
  180. io.set('store', new RedisStore({
  181. redisPub : pub,
  182. redisSub : sub,
  183. redisClient : client
  184. }));
  185. io.sockets.on('connection',function(socket){
  186. socket.on('join',function(data){
  187. socket.join(data.name);
  188. data.title = data.name;
  189. socket.emit('join',{
  190. name: data.name
  191. });
  192. sendUserList(data.name);
  193. socket.get('nick',function(e,nick){
  194. logger.debug(nick+' joined '+data.name);
  195. io.sockets.in(data.name).emit('message',{
  196. message: nick+' joined the channel',
  197. room: data.name,
  198. from: 0
  199. });
  200. });
  201. });
  202. socket.on('part',function(data){
  203. socket.leave(data.name);
  204. socket.get('nick',function(e,nick){
  205. logger.debug(nick+' left '+data.name);
  206. sendUserList(data.name);
  207. });
  208. });
  209. socket.on('disconnect',function(data){
  210. var rooms = io.sockets.manager.rooms,
  211. i,
  212. room;
  213. for(i in rooms){
  214. if(rooms[i] != '' && typeof rooms[i] == 'string'){
  215. try{
  216. room = rooms[i].substr(1);
  217. }catch(e){}
  218. sendUserList(room);
  219. }
  220. }
  221. });
  222. socket.on('message',function(data){
  223. logger.debug('message sent to '+data.room);
  224. io.sockets.in(data.room).emit('message',data);
  225. });
  226. socket.on('echo',function(data){
  227. logger.debug('echoing to '+data.room);
  228. socket.emit('message',data);
  229. });
  230. socket.on('names',function(data){
  231. var sockets = io.sockets.clients(data.name),
  232. i;
  233. runWithUserList(data.name,function(users){
  234. var temp = [],i;
  235. for(i in users) i && temp.push(users[i]);
  236. users = temp;
  237. socket.emit('message',{
  238. message: data.name+" users:\n\t\t"+users.join("\n\t\t"),
  239. room: data.name,
  240. from: 0
  241. });
  242. sendUserList(data.name);
  243. });
  244. });
  245. socket.on('auth',function(data){
  246. logger.info(data.nick+' registered');
  247. // TODO - authorize
  248. socket.set('nick',data.nick.substr(0,12));
  249. socket.emit('authorized',{
  250. nick: data.nick.substr(0,12)
  251. });
  252. });
  253. var runWithUserList = function(room,callback){
  254. var sockets = io.sockets.clients(room),
  255. i = 0,
  256. ret = [],
  257. getNext = function(){
  258. if(i < sockets.length){
  259. sockets[i].get('nick',function(e,nick){
  260. if(e){
  261. logger.error(e);
  262. }else if(!inArray(ret,nick)){
  263. logger.debug(room+' '+nick);
  264. ret.push(nick);
  265. }
  266. i++;
  267. getNext();
  268. });
  269. }else{
  270. callback(ret);
  271. }
  272. };
  273. getNext();
  274. },
  275. inArray = function(arr,val){
  276. for(var i in arr){
  277. if(arr[i] == val){
  278. return true;
  279. }
  280. }
  281. return false;
  282. },
  283. sendUserList = function(room){
  284. if(typeof room != 'undefined'){
  285. runWithUserList(room,function(users){
  286. io.sockets.in(room).emit('names',{
  287. room: room,
  288. names: users
  289. });
  290. });
  291. }
  292. };
  293. });
  294. }
  295. process.on('uncaughtException',function(e){
  296. if(typeof logger != 'undefined'){
  297. logger.error(e);
  298. }else{
  299. console.error(e);
  300. }
  301. });