OmnomIRC.js 7.7 KB

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