8e0f29d740efb08b7f6ff02c9cb9fc3d0b80b125
1 var fs
= require('fs'),
3 util
= require('util'),
4 winston
= require('winston'),
5 WebListener
= require('./weblistener.js'),
6 config
= require('./configuration.js'),
7 modules
= require('./modules.js'),
8 Identd
= require('./identd.js'),
9 Proxy
= require('./proxy.js'),
10 ControlInterface
= require('./controlinterface.js');
14 process
.chdir(__dirname
+ '/../');
17 var conf_switch
= argv
.indexOf('-c');
18 if (conf_switch
!== -1) {
19 if (argv
[conf_switch
+ 1]) {
20 return config
.loadConfig(argv
[conf_switch
+ 1]);
29 // If we're not running in the forground and we have a log file.. switch
30 // console.log to output to a file
31 if (process
.argv
.indexOf('-f') === -1 && global
.config
&& global
.config
.log
) {
33 var log_file_name
= global
.config
.log
;
35 if (log_file_name
[0] !== '/') {
36 log_file_name
= __dirname
+ '/../' + log_file_name
;
39 winston
.add(winston
.transports
.File
, { filename
: log_file_name
, json
: false});
40 winston
.remove(winston
.transports
.Console
);
46 // Make sure we have a valid config file and at least 1 server
47 if (!global
.config
|| Object
.keys(global
.config
).length
=== 0) {
48 //console.log('Couldn\'t find a valid config.js file (Did you copy the config.example.js file yet?)');
49 winston
.error('Couldn\'t find a valid config.js file (Did you copy the config.example.js file yet?)');
53 if ((!global
.config
.servers
) || (global
.config
.servers
.length
< 1)) {
54 //console.log('No servers defined in config file');
55 winston
.error('No servers defined in config file');
62 // Create a plugin interface
63 global
.modules
= new modules
.Publisher();
65 // Register as the active interface
66 modules
.registerPublisher(global
.modules
);
68 // Load any modules in the config
69 if (global
.config
.module_dir
) {
70 (global
.config
.modules
|| []).forEach(function (module_name
) {
71 if (modules
.load(module_name
)) {
72 //console.log('Module ' + module_name + ' loaded successfuly');
73 winston
.info('Module %s loaded successfully', module_name
);
75 //console.log('Module ' + module_name + ' failed to load');
76 winston
.warn('Module %s failed to load', module_name
);
84 // Holder for all the connected clients
86 clients
: Object
.create(null),
87 addresses
: Object
.create(null),
89 // Local and foriegn port pairs for identd lookups
90 // {'65483_6667': client_obj, '54356_6697': client_obj}
93 add: function (client
) {
94 this.clients
[client
.hash
] = client
;
95 if (typeof this.addresses
[client
.real_address
] === 'undefined') {
96 this.addresses
[client
.real_address
] = Object
.create(null);
98 this.addresses
[client
.real_address
][client
.hash
] = client
;
101 remove: function (client
) {
102 if (typeof this.clients
[client
.hash
] !== 'undefined') {
103 delete this.clients
[client
.hash
];
104 delete this.addresses
[client
.real_address
][client
.hash
];
105 if (Object
.keys(this.addresses
[client
.real_address
]).length
< 1) {
106 delete this.addresses
[client
.real_address
];
111 numOnAddress: function (addr
) {
112 if (typeof this.addresses
[addr
] !== 'undefined') {
113 return Object
.keys(this.addresses
[addr
]).length
;
119 broadcastKiwiCommand: function (command
, data
, callback
) {
122 // Get an array of clients for us to work with
123 for (var client
in global
.clients
.clients
) {
124 clients
.push(global
.clients
.clients
[client
]);
128 // Sending of the command in batches
129 var sendCommandBatch = function (list
) {
130 var batch_size
= 100,
133 if (list
.length
>= batch_size
) {
134 // If we have more clients than our batch size, call ourself with the next batch
135 setTimeout(function () {
136 sendCommandBatch(list
.slice(batch_size
));
142 cutoff
= list
.length
;
145 list
.slice(0, cutoff
).forEach(function (client
) {
146 if (!client
.disposed
) {
147 client
.sendKiwiCommand(command
, data
);
151 if (cutoff
=== list
.length
&& typeof callback
=== 'function') {
156 sendCommandBatch(clients
);
161 servers
: Object
.create(null),
163 addConnection: function (connection
) {
164 var host
= connection
.irc_host
.hostname
;
165 if (!this.servers
[host
]) {
166 this.servers
[host
] = [];
168 this.servers
[host
].push(connection
);
171 removeConnection: function (connection
) {
172 var host
= connection
.irc_host
.hostname
173 if (this.servers
[host
]) {
174 this.servers
[host
] = _
.without(this.servers
[host
], connection
);
175 if (this.servers
[host
].length
=== 0) {
176 delete this.servers
[host
];
181 numOnHost: function (host
) {
182 if (this.servers
[host
]) {
183 return this.servers
[host
].length
;
193 * When a new config is loaded, send out an alert to the clients so
194 * so they can reload it
196 config
.on('loaded', function () {
197 global
.clients
.broadcastKiwiCommand('reconfig');
205 if (global
.config
.identd
&& global
.config
.identd
.enabled
) {
206 var identd_resolve_user = function(port_here
, port_there
) {
207 var key
= port_here
.toString() + '_' + port_there
.toString();
209 if (typeof global
.clients
.port_pairs
[key
] == 'undefined') {
213 return global
.clients
.port_pairs
[key
].username
;
216 var identd_server
= new Identd({
217 bind_addr
: global
.config
.identd
.address
,
218 bind_port
: global
.config
.identd
.port
,
219 user_id
: identd_resolve_user
222 identd_server
.start();
233 // Start up a weblistener for each found in the config
234 _
.each(global
.config
.servers
, function (server
) {
235 if (server
.type
== 'proxy') {
236 // Start up a kiwi proxy server
237 var serv
= new Proxy
.ProxyServer();
238 serv
.listen(server
.port
, server
.address
, server
);
240 serv
.on('listening', function() {
241 //console.log('Kiwi proxy listening on %s:%s %s SSL', server.address, server.port, (server.ssl ? 'with' : 'without'));
242 winston
.info('Kiwi proxy listening on %s:%s %s SSL', server
.address
, server
.port
, (server
.ssl
? 'with' : 'without'));
245 serv
.on('socket_connected', function(pipe
) {
246 // SSL connections have the raw socket as a property
247 var socket
= pipe
.irc_socket
.socket
?
248 pipe
.irc_socket
.socket
:
251 pipe
.identd_pair
= socket
.localPort
.toString() + '_' + socket
.remotePort
.toString();
252 global
.clients
.port_pairs
[pipe
.identd_pair
] = pipe
.meta
;
255 serv
.on('connection_close', function(pipe
) {
256 delete global
.clients
.port_pairs
[pipe
.identd_pair
];
260 // Start up a kiwi web server
261 var wl
= new WebListener(server
, global
.config
.transports
);
263 wl
.on('connection', function (client
) {
267 wl
.on('client_dispose', function (client
) {
268 clients
.remove(client
);
271 wl
.on('listening', function () {
272 //console.log('Listening on %s:%s %s SSL', server.address, server.port, (server.ssl ? 'with' : 'without'));
273 winston
.info('Listening on %s:%s %s SSL', server
.address
, server
.port
, (server
.ssl
? 'with' : 'without'));
274 webListenerRunning();
277 wl
.on('error', function (err
) {
278 //console.log('Error listening on %s:%s: %s', server.address, server.port, err.code);
279 winston
.info('Error listening on %s:%s: %s', server
.address
, server
.port
, err
.code
);
280 // TODO: This should probably be refactored. ^JA
281 webListenerRunning();
286 // Once all the listeners are listening, set the processes UID/GID
287 var num_listening
= 0;
288 function webListenerRunning() {
290 if (num_listening
=== global
.config
.servers
.length
) {
303 process
.title
= 'kiwiirc';
306 function setProcessUid() {
307 if ((global
.config
.group
) && (global
.config
.group
!== '')) {
308 process
.setgid(global
.config
.group
);
310 if ((global
.config
.user
) && (global
.config
.user
!== '')) {
311 process
.setuid(global
.config
.user
);
316 // Make sure Kiwi doesn't simply quit on an exception
317 process
.on('uncaughtException', function (e
) {
318 //console.log('[Uncaught exception] ' + e);
319 //console.log(e.stack);
320 winston
.error('[Uncaught exception] %s', e
, {stack
: e
.stack
});
324 process
.on('SIGUSR1', function() {
325 if (config
.loadConfig()) {
326 //console.log('New config file loaded');
327 winston
.info('New config file loaded');
329 //console.log("No new config file was loaded");
330 winston
.info('No new config file was loaded');
335 process
.on('SIGUSR2', function() {
336 //console.log('Connected clients: ' + _.size(global.clients.clients).toString());
337 //console.log('Num. remote hosts: ' + _.size(global.clients.addresses).toString());
338 winston
.info('Connected clients: %s', _
.size(global
.clients
.clients
));
339 winston
.info('Num. remote hosts: %s', _
.size(global
.clients
.addresses
));
344 * Listen for runtime commands
346 process
.stdin
.resume();
347 new ControlInterface(process
.stdin
, process
.stdout
, {prompt
: ''});