1 var fs
= require('fs'),
3 util
= require('util'),
4 WebListener
= require('./weblistener.js'),
5 config
= require('./configuration.js'),
6 modules
= require('./modules.js'),
7 Identd
= require('./identd.js'),
8 Proxy
= require('./proxy.js'),
9 ControlInterface
= require('./controlinterface.js');
13 process
.chdir(__dirname
+ '/../');
17 // If we're not running in the forground and we have a log file.. switch
18 // console.log to output to a file
19 if (process
.argv
.indexOf('-f') === -1 && global
.config
&& global
.config
.log
) {
21 var log_file_name
= global
.config
.log
;
23 if (log_file_name
[0] !== '/') {
24 log_file_name
= __dirname
+ '/../' + log_file_name
;
29 console
.log = function() {
30 var logfile
= fs
.openSync(log_file_name
, 'a'),
33 out
= util
.format
.apply(util
, arguments
);
35 // Make sure we out somthing to log and we have an open file
36 if (!out
|| !logfile
) return;
39 fs
.writeSync(logfile
, out
, null);
41 fs
.closeSync(logfile
);
48 // Make sure we have a valid config file and at least 1 server
49 if (!global
.config
|| Object
.keys(global
.config
).length
=== 0) {
50 console
.log('Couldn\'t find a valid config.js file (Did you copy the config.example.js file yet?)');
54 if ((!global
.config
.servers
) || (global
.config
.servers
.length
< 1)) {
55 console
.log('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');
74 console
.log('Module ' + module_name
+ ' failed to load');
82 // Holder for all the connected clients
84 clients
: Object
.create(null),
85 addresses
: Object
.create(null),
87 // Local and foriegn port pairs for identd lookups
88 // {'65483_6667': client_obj, '54356_6697': client_obj}
91 add: function (client
) {
92 this.clients
[client
.hash
] = client
;
93 if (typeof this.addresses
[client
.real_address
] === 'undefined') {
94 this.addresses
[client
.real_address
] = Object
.create(null);
96 this.addresses
[client
.real_address
][client
.hash
] = client
;
99 remove: function (client
) {
100 if (typeof this.clients
[client
.hash
] !== 'undefined') {
101 delete this.clients
[client
.hash
];
102 delete this.addresses
[client
.real_address
][client
.hash
];
103 if (Object
.keys(this.addresses
[client
.real_address
]).length
< 1) {
104 delete this.addresses
[client
.real_address
];
109 numOnAddress: function (addr
) {
110 if (typeof this.addresses
[addr
] !== 'undefined') {
111 return Object
.keys(this.addresses
[addr
]).length
;
117 broadcastKiwiCommand: function (command
, data
, callback
) {
120 // Get an array of clients for us to work with
121 for (var client
in global
.clients
.clients
) {
122 clients
.push(global
.clients
.clients
[client
]);
126 // Sending of the command in batches
127 var sendCommandBatch = function (list
) {
128 var batch_size
= 100,
131 if (list
.length
>= batch_size
) {
132 // If we have more clients than our batch size, call ourself with the next batch
133 setTimeout(function () {
134 sendCommandBatch(list
.slice(batch_size
));
140 cutoff
= list
.length
;
143 list
.slice(0, cutoff
).forEach(function (client
) {
144 if (!client
.disposed
) {
145 client
.sendKiwiCommand(command
, data
);
149 if (cutoff
=== list
.length
&& typeof callback
=== 'function') {
154 sendCommandBatch(clients
);
159 servers
: Object
.create(null),
161 addConnection: function (connection
) {
162 var host
= connection
.irc_host
.hostname
;
163 if (!this.servers
[host
]) {
164 this.servers
[host
] = [];
166 this.servers
[host
].push(connection
);
169 removeConnection: function (connection
) {
170 var host
= connection
.irc_host
.hostname
171 if (this.servers
[host
]) {
172 this.servers
[host
] = _
.without(this.servers
[host
], connection
);
173 if (this.servers
[host
].length
=== 0) {
174 delete this.servers
[host
];
179 numOnHost: function (host
) {
180 if (this.servers
[host
]) {
181 return this.servers
[host
].length
;
191 * When a new config is loaded, send out an alert to the clients so
192 * so they can reload it
194 config
.on('loaded', function () {
195 global
.clients
.broadcastKiwiCommand('reconfig');
203 if (global
.config
.identd
&& global
.config
.identd
.enabled
) {
204 var identd_resolve_user = function(port_here
, port_there
) {
205 var key
= port_here
.toString() + '_' + port_there
.toString();
207 if (typeof global
.clients
.port_pairs
[key
] == 'undefined') {
211 return global
.clients
.port_pairs
[key
].username
;
214 var identd_server
= new Identd({
215 bind_addr
: global
.config
.identd
.address
,
216 bind_port
: global
.config
.identd
.port
,
217 user_id
: identd_resolve_user
220 identd_server
.start();
231 // Start up a weblistener for each found in the config
232 _
.each(global
.config
.servers
, function (server
) {
233 if (server
.type
== 'proxy') {
234 // Start up a kiwi proxy server
235 var serv
= new Proxy
.ProxyServer();
236 serv
.listen(server
.port
, server
.address
, server
);
238 serv
.on('listening', function() {
239 console
.log('Kiwi proxy listening on %s:%s %s SSL', server
.address
, server
.port
, (server
.ssl
? 'with' : 'without'));
242 serv
.on('connection_open', function(pipe
) {
243 pipe
.identd_pair
= pipe
.irc_socket
.localPort
.toString() + '_' + pipe
.irc_socket
.remotePort
.toString();
244 console
.log('[IDENTD] opened ' + pipe
.identd_pair
);
245 global
.clients
.port_pairs
[pipe
.identd_pair
] = pipe
.meta
;
248 serv
.on('connection_close', function(pipe
) {
249 console
.log('[IDENTD] closed ' + pipe
.identd_pair
);
250 delete global
.clients
.port_pairs
[pipe
.identd_pair
];
254 // Start up a kiwi web server
255 var wl
= new WebListener(server
, global
.config
.transports
);
257 wl
.on('connection', function (client
) {
261 wl
.on('client_dispose', function (client
) {
262 clients
.remove(client
);
265 wl
.on('listening', function () {
266 console
.log('Listening on %s:%s %s SSL', server
.address
, server
.port
, (server
.ssl
? 'with' : 'without'));
267 webListenerRunning();
270 wl
.on('error', function (err
) {
271 console
.log('Error listening on %s:%s: %s', server
.address
, server
.port
, err
.code
);
272 // TODO: This should probably be refactored. ^JA
273 webListenerRunning();
278 // Once all the listeners are listening, set the processes UID/GID
279 var num_listening
= 0;
280 function webListenerRunning() {
282 if (num_listening
=== global
.config
.servers
.length
) {
295 process
.title
= 'kiwiirc';
298 function setProcessUid() {
299 if ((global
.config
.group
) && (global
.config
.group
!== '')) {
300 process
.setgid(global
.config
.group
);
302 if ((global
.config
.user
) && (global
.config
.user
!== '')) {
303 process
.setuid(global
.config
.user
);
308 // Make sure Kiwi doesn't simply quit on an exception
309 process
.on('uncaughtException', function (e
) {
310 console
.log('[Uncaught exception] ' + e
);
311 console
.log(e
.stack
);
315 process
.on('SIGUSR1', function() {
316 if (config
.loadConfig()) {
317 console
.log('New config file loaded');
319 console
.log("No new config file was loaded");
324 process
.on('SIGUSR2', function() {
325 console
.log('Connected clients: ' + _
.size(global
.clients
.clients
).toString());
326 console
.log('Num. remote hosts: ' + _
.size(global
.clients
.addresses
).toString());
331 * Listen for runtime commands
333 process
.stdin
.resume();
334 new ControlInterface(process
.stdin
, process
.stdout
, {prompt
: ''});