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