Merge branch 'development'
[KiwiIRC.git] / server / irc / connection.js
1 var net = require('net'),
2 tls = require('tls'),
3 events = require('events'),
4 util = require('util'),
5 _ = require('underscore');
6
7 var IrcConnection = function (hostname, port, ssl, nick, user, pass) {
8 var that = this;
9 events.EventEmitter.call(this);
10
11 if (ssl) {
12 this.socket = tls.connect({
13 host: hostname,
14 port: port,
15 rejectUnauthorized: global.config.reject_unauthorised_certificates
16 }, function () {
17 connect_handler.apply(that, arguments);
18 });
19 } else {
20 this.socket = net.createConnection(port, hostname);
21 this.socket.on('connect', function () {
22 connect_handler.apply(that, arguments);
23 });
24 }
25
26 this.socket.on('error', function (event) {
27 that.emit('error', event);
28 });
29
30 this.socket.setEncoding('utf-8');
31
32 this.socket.on('data', function () {
33 parse.apply(that, arguments);
34 });
35
36 this.socket.on('close', function () {
37 that.emit('close');
38 });
39
40 this.connected = false;
41 this.registered = false;
42 this.nick = nick;
43 this.user = user;
44 this.irc_host = {hostname: hostname, port: port};
45 this.ssl = !(!ssl);
46 this.options = Object.create(null);
47
48 this.password = pass;
49 this.hold_last = false;
50 this.held_data = '';
51 };
52 util.inherits(IrcConnection, events.EventEmitter);
53
54 module.exports.IrcConnection = IrcConnection;
55
56
57 IrcConnection.prototype.write = function (data, callback) {
58 write.call(this, data + '\r\n', 'utf-8', callback);
59 };
60
61 IrcConnection.prototype.end = function (data, callback) {
62 end.call(this, data + '\r\n', 'utf-8', callback);
63 };
64
65 IrcConnection.prototype.dispose = function () {
66 this.removeAllListeners();
67 };
68
69
70 var write = function (data, encoding, callback) {
71 this.socket.write(data, encoding, callback);
72 };
73
74 var end = function (data, encoding, callback) {
75 this.socket.end(data, encoding, callback);
76 };
77
78
79 var connect_handler = function () {
80 var that = this,
81 connect_data;
82
83 // Build up data to be used for webirc/etc detection
84 connect_data = {
85 user: this.user,
86 nick: this.nick,
87 realname: '[www.kiwiirc.com] ' + this.nick,
88 username: this.nick.replace(/[^0-9a-zA-Z\-_.]/, ''),
89 irc_host: this.irc_host
90 };
91
92 // Let the webirc/etc detection modify any required parameters
93 connect_data = findWebIrc(connect_data);
94
95 // Send any initial data for webirc/etc
96 if (connect_data.prepend_data) {
97 _.each(connect_data.prepend_data, function(data) {
98 that.write(data);
99 });
100 }
101
102 if (this.password) {
103 this.write('PASS ' + this.password);
104 }
105
106 this.write('NICK ' + connect_data.nick);
107 this.write('USER ' + connect_data.username + ' 0 0 :' + connect_data.realname);
108
109 this.connected = true;
110 this.emit('connected');
111 };
112
113
114
115
116 function findWebIrc(connect_data) {
117 var webirc_pass = global.config.webirc_pass;
118 var ip_as_username = global.config.ip_as_username;
119 var tmp;
120
121 // Do we have a WEBIRC password for this?
122 if (webirc_pass && webirc_pass[connect_data.irc_host.hostname]) {
123 tmp = 'WEBIRC ' + webirc_pass[connect_data.irc_host.hostname] + ' KiwiIRC ';
124 tmp += connect_data.user.hostname + ' ' + connect_data.user.address;
125 connect_data.prepend_data = [tmp];
126 }
127
128
129 // Check if we need to pass the users IP as its username/ident
130 if (ip_as_username && ip_as_username.indexOf(connect_data.irc_host.hostname) > -1) {
131 // Get a hex value of the clients IP
132 connect_data.username = connect_data.user.address.split('.').map(function(i, idx){
133 return parseInt(i, 10).toString(16);
134 }).join('');
135
136 }
137
138 return connect_data;
139 }
140
141
142
143 parse_regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i;
144 var parse = function (data) {
145 var i,
146 msg,
147 msg2,
148 trm;
149
150 if ((this.hold_last) && (this.held_data !== '')) {
151 data = this.held_data + data;
152 this.hold_last = false;
153 this.held_data = '';
154 }
155 if (data.substr(-1) !== '\n') {
156 this.hold_last = true;
157 }
158 data = data.split("\n");
159 for (i = 0; i < data.length; i++) {
160 if (data[i]) {
161 if ((this.hold_last) && (i === data.length - 1)) {
162 this.held_data = data[i];
163 break;
164 }
165
166 // We have a complete line of data, parse it!
167 msg = parse_regex.exec(data[i].replace(/^\r+|\r+$/, ''));
168 if (msg) {
169 msg = {
170 prefix: msg[1],
171 nick: msg[2],
172 ident: msg[3],
173 hostname: msg[4] || '',
174 command: msg[5],
175 params: msg[6] || '',
176 trailing: (msg[7]) ? msg[7].trim() : ''
177 };
178 msg.params = msg.params.split(' ');
179
180 this.emit('irc_' + msg.command.toUpperCase(), msg);
181 } else {
182 console.log("Malformed IRC line: " + data[i].replace(/^\r+|\r+$/, ''));
183 }
184 }
185 }
186 };