Commit | Line | Data |
---|---|---|
2a8e95d1 D |
1 | var net = require('net'), |
2 | tls = require('tls'), | |
3 | events = require('events'), | |
15fefff7 | 4 | util = require('util'), |
795014b1 | 5 | _ = require('underscore'); |
2a8e95d1 | 6 | |
15fefff7 | 7 | var IrcConnection = function (hostname, port, ssl, nick, user, pass) { |
2a8e95d1 D |
8 | var that = this; |
9 | events.EventEmitter.call(this); | |
10 | ||
11 | if (ssl) { | |
36108ca9 D |
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 | }); | |
2a8e95d1 D |
19 | } else { |
20 | this.socket = net.createConnection(port, hostname); | |
21 | this.socket.on('connect', function () { | |
22 | connect_handler.apply(that, arguments); | |
23 | }); | |
24 | } | |
25 | ||
d2d91c10 D |
26 | this.socket.on('error', function (event) { |
27 | that.emit('error', event); | |
2a8e95d1 D |
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; | |
eec8c71e | 42 | this.cap_negotiation = true; |
2a8e95d1 D |
43 | this.nick = nick; |
44 | this.user = user; | |
15fefff7 | 45 | this.irc_host = {hostname: hostname, port: port}; |
2a8e95d1 D |
46 | this.ssl = !(!ssl); |
47 | this.options = Object.create(null); | |
eec8c71e | 48 | this.cap = {requested: [], enabled: []}; |
7dfe47c6 | 49 | this.sasl = false; |
2a8e95d1 | 50 | |
2a8e95d1 D |
51 | this.password = pass; |
52 | this.hold_last = false; | |
53 | this.held_data = ''; | |
54 | }; | |
55 | util.inherits(IrcConnection, events.EventEmitter); | |
56 | ||
57 | module.exports.IrcConnection = IrcConnection; | |
58 | ||
59 | ||
60 | IrcConnection.prototype.write = function (data, callback) { | |
61 | write.call(this, data + '\r\n', 'utf-8', callback); | |
62 | }; | |
63 | ||
64 | IrcConnection.prototype.end = function (data, callback) { | |
2a8e95d1 D |
65 | end.call(this, data + '\r\n', 'utf-8', callback); |
66 | }; | |
67 | ||
c08717da D |
68 | IrcConnection.prototype.dispose = function () { |
69 | this.removeAllListeners(); | |
70 | }; | |
71 | ||
72 | ||
2a8e95d1 D |
73 | var write = function (data, encoding, callback) { |
74 | this.socket.write(data, encoding, callback); | |
75 | }; | |
76 | ||
77 | var end = function (data, encoding, callback) { | |
78 | this.socket.end(data, encoding, callback); | |
79 | }; | |
80 | ||
81 | ||
82 | var connect_handler = function () { | |
15fefff7 D |
83 | var that = this, |
84 | connect_data; | |
85 | ||
86 | // Build up data to be used for webirc/etc detection | |
87 | connect_data = { | |
88 | user: this.user, | |
89 | nick: this.nick, | |
90 | realname: '[www.kiwiirc.com] ' + this.nick, | |
91 | username: this.nick.replace(/[^0-9a-zA-Z\-_.]/, ''), | |
92 | irc_host: this.irc_host | |
93 | }; | |
94 | ||
95 | // Let the webirc/etc detection modify any required parameters | |
96 | connect_data = findWebIrc(connect_data); | |
97 | ||
98 | // Send any initial data for webirc/etc | |
99 | if (connect_data.prepend_data) { | |
100 | _.each(connect_data.prepend_data, function(data) { | |
101 | that.write(data); | |
102 | }); | |
2a8e95d1 | 103 | } |
15fefff7 | 104 | |
eec8c71e | 105 | this.write('CAP LS'); |
7dfe47c6 JA |
106 | |
107 | this.registration_timeout = setTimeout(function () { | |
3dd5ff0a | 108 | that.register(); |
7dfe47c6 | 109 | }, 1000); |
2a8e95d1 D |
110 | |
111 | this.connected = true; | |
2a8e95d1 D |
112 | this.emit('connected'); |
113 | }; | |
114 | ||
7dfe47c6 JA |
115 | IrcConnection.prototype.register = function () { |
116 | if (this.registration_timeout !== null) { | |
117 | clearTimeout(this.registeration_timeout); | |
118 | this.registration_timeout = null; | |
119 | } | |
285bffb5 | 120 | if ((this.password) && (!this.sasl)) { |
7dfe47c6 JA |
121 | this.write('PASS ' + this.password); |
122 | } | |
123 | this.write('NICK ' + this.nick); | |
124 | this.write('USER ' + this.nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + '[www.kiwiirc.com] ' + this.nick); | |
125 | if (this.cap_negotation) { | |
126 | this.write('CAP END'); | |
127 | } | |
128 | }; | |
2a8e95d1 | 129 | |
15fefff7 D |
130 | |
131 | ||
132 | function findWebIrc(connect_data) { | |
b737610b JA |
133 | var webirc_pass = global.config.webirc_pass; |
134 | var ip_as_username = global.config.ip_as_username; | |
15fefff7 D |
135 | var tmp; |
136 | ||
137 | // Do we have a WEBIRC password for this? | |
138 | if (webirc_pass && webirc_pass[connect_data.irc_host.hostname]) { | |
139 | tmp = 'WEBIRC ' + webirc_pass[connect_data.irc_host.hostname] + ' KiwiIRC '; | |
140 | tmp += connect_data.user.hostname + ' ' + connect_data.user.address; | |
141 | connect_data.prepend_data = [tmp]; | |
142 | } | |
143 | ||
144 | ||
145 | // Check if we need to pass the users IP as its username/ident | |
146 | if (ip_as_username && ip_as_username.indexOf(connect_data.irc_host.hostname) > -1) { | |
147 | // Get a hex value of the clients IP | |
148 | connect_data.username = connect_data.user.address.split('.').map(function(i, idx){ | |
149 | return parseInt(i, 10).toString(16); | |
150 | }).join(''); | |
151 | ||
152 | } | |
153 | ||
154 | return connect_data; | |
155 | } | |
156 | ||
157 | ||
158 | ||
16e717f5 | 159 | parse_regex = /^(?:(?:(?:(@[^ ]+) )?):(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i; |
2a8e95d1 D |
160 | var parse = function (data) { |
161 | var i, | |
162 | msg, | |
16e717f5 JA |
163 | msg2, |
164 | trm, | |
165 | j, | |
166 | tags = [], | |
167 | tag; | |
2a8e95d1 D |
168 | |
169 | if ((this.hold_last) && (this.held_data !== '')) { | |
170 | data = this.held_data + data; | |
171 | this.hold_last = false; | |
172 | this.held_data = ''; | |
173 | } | |
174 | if (data.substr(-1) !== '\n') { | |
175 | this.hold_last = true; | |
176 | } | |
177 | data = data.split("\n"); | |
178 | for (i = 0; i < data.length; i++) { | |
179 | if (data[i]) { | |
180 | if ((this.hold_last) && (i === data.length - 1)) { | |
181 | this.held_data = data[i]; | |
182 | break; | |
183 | } | |
184 | ||
185 | // We have a complete line of data, parse it! | |
186 | msg = parse_regex.exec(data[i].replace(/^\r+|\r+$/, '')); | |
187 | if (msg) { | |
16e717f5 JA |
188 | if (msg[1]) { |
189 | tags = msg[1].split(';'); | |
190 | for (j = 0; j < tags.length; j++) { | |
191 | tag = tags[j].split('='); | |
192 | tags[j] = {tag: tag[0], value: tag[1]}; | |
193 | } | |
194 | } | |
2a8e95d1 | 195 | msg = { |
16e717f5 JA |
196 | tags: tags, |
197 | prefix: msg[2], | |
198 | nick: msg[3], | |
199 | ident: msg[4], | |
200 | hostname: msg[5] || '', | |
201 | command: msg[6], | |
202 | params: msg[7] || '', | |
203 | trailing: (msg[8]) ? msg[8].trim() : '' | |
2a8e95d1 D |
204 | }; |
205 | msg.params = msg.params.split(' '); | |
206 | ||
207 | this.emit('irc_' + msg.command.toUpperCase(), msg); | |
208 | } else { | |
209 | console.log("Malformed IRC line: " + data[i].replace(/^\r+|\r+$/, '')); | |
210 | } | |
211 | } | |
212 | } | |
213 | }; |