2 * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
82 static void recv_line(irc_server_t *srv, const char *line)
84 static char src[IRC_LINE]; // (:([^ ]+) +)?
85 static char cmd[IRC_LINE]; // (([A-Z0-9]+) +)
86 static char dst[IRC_LINE]; // (([^ ]+)[= ]+)?
87 static char arg[IRC_LINE]; // (([^: ]+) *)?
88 static char msg[IRC_LINE]; // (:(.*))?
89 static char from[IRC_LINE];
95 src[0] = cmd[0] = dst[0] = arg[0] = msg[0] = from[0] = '\0';
100 for (i = 0; *c && *c != ' '; i++)
105 for (i = 0; src[i] && src[i] != '!' && src[i] != ' '; i++)
111 for (i = 0; isalnum(*c); i++)
118 if ((*c && *c != ' ') &&
119 (strchr(c+1, ' ') || strchr(c+1, '='))) {
120 for (i = 0; *c && *c != ' '; i++)
122 while (*c == '=' || *c == ' ')
128 if (*c && *c != ':' && *c != ' ') {
129 for (i = 0; *c && *c != ' '; i++)
144 debug("got line: [%s]", line);
145 //debug(" src %s", src);
146 //debug(" cmd %s", cmd);
147 //debug(" dst %s", dst);
148 //debug(" arg %s", arg);
149 //debug(" msg %s", msg);
152 if (srv->state == IRC_RECV_TLS) {
153 if (match(cmd, "CAP") && match(arg, "ACK")) {
154 chat_notice(NULL, from, "Start TLS proceeding");
155 srv->state = IRC_SEND_STARTTLS;
157 if (match(cmd, "CAP") && match(arg, "NAK")) {
158 chat_notice(NULL, from, "Start TLS unsupported");
159 srv->state = IRC_SEND_END;
162 if (srv->state == IRC_RECV_STARTTLS) {
163 if (prefix(msg, "STARTTLS successful", NULL))
164 srv->state = IRC_ENCRYPT;
167 /* SASL Authentication */
168 if (srv->state == IRC_RECV_SASL) {
169 if (match(cmd, "CAP") && match(arg, "ACK")) {
170 chat_notice(NULL, from, "SASL auth proceeding");
171 srv->state = IRC_SEND_PLAIN;
173 if (match(cmd, "CAP") && match(arg, "NAK")) {
174 chat_notice(NULL, from, "SASL auth unsupported");
175 srv->state = IRC_SEND_END;
178 if (srv->state == IRC_RECV_STATUS) {
179 if (match(cmd, "903")) {
180 chat_notice(NULL, from, "SASL auth succeeded");
181 srv->state = IRC_SEND_END;
183 if (match(cmd, "904") ||
187 chat_notice(NULL, from, "SASL auth failed");
188 srv->state = IRC_DEAD;
192 /* Connection setup */
193 if (srv->state == IRC_RECV_WELCOME)
194 if (match(cmd, "001") && strstr(msg, "Welcome"))
195 srv->state = IRC_JOIN;
197 /* Receive messages */
198 if (srv->state == IRC_READY) {
199 if (match(cmd, "PING"))
200 net_print(&srv->net, "PING %s\n", msg);
201 if (match(cmd, "PRIVMSG"))
202 chat_recv(find_channel(dst), from, msg);
205 /* Receive notices */
206 if (match(cmd, "NOTICE") ||
211 chat_notice(NULL, from, "%s", msg);
214 static void on_recv(void *_srv, char *buf, int len)
216 static char plain[IRC_LINE];
217 static char coded[IRC_LINE];
218 irc_server_t *srv = _srv;
221 for (int i = 0; i < len; i++) {
222 if (srv->pos < IRC_LINE) {
223 srv->line[srv->pos] = buf[i];
226 if (buf[i] == '\n' || buf[i] == '\r') {
227 srv->line[srv->pos-1] = '\0';
229 recv_line(srv, srv->line);
235 if (srv->state == IRC_ENCRYPT) {
236 net_encrypt(&srv->net);
237 srv->state = IRC_SEND_SASL;
241 if (srv->state == IRC_SEND_TLS) {
242 if (net_print(&srv->net, "CAP REQ :tls\n"))
243 srv->state = IRC_RECV_TLS;
245 if (srv->state == IRC_SEND_STARTTLS) {
246 if (net_print(&srv->net, "STARTTLS\n"))
247 srv->state = IRC_RECV_STARTTLS;
250 /* SASL authentication */
251 if (srv->state == IRC_SEND_SASL) {
252 if (net_print(&srv->net, "CAP REQ :sasl\n"))
253 srv->state = IRC_RECV_SASL;
255 if (srv->state == IRC_SEND_PLAIN) {
256 if (net_print(&srv->net, "AUTHENTICATE PLAIN\n"))
257 srv->state = IRC_SEND_AUTH;
259 if (srv->state == IRC_SEND_AUTH) {
260 len = snprintf(plain, IRC_LINE, "%s%c%s%c%s",
261 srv->auth, '\0', srv->auth, '\0', srv->pass);
262 len = base64(plain, len, coded, IRC_LINE);
263 if (net_print(&srv->net, "AUTHENTICATE %.*s\n", len, coded))
264 srv->state = IRC_RECV_STATUS;
266 if (srv->state == IRC_SEND_END) {
267 if (net_print(&srv->net, "CAP END\n"))
268 srv->state = IRC_SEND_USER;
271 /* Connection setup */
272 if (srv->state == IRC_SEND_USER) {
273 if (net_print(&srv->net, "USER %s %s %s :%s\n",
274 getenv("USER") ?: "lameuser",
275 get_hostname(), srv->host, srv->nick))
276 srv->state = IRC_SEND_NICK;
278 if (srv->state == IRC_SEND_NICK) {
279 if (net_print(&srv->net, "NICK %s\n", srv->nick))
280 srv->state = IRC_RECV_WELCOME;
282 if (srv->state == IRC_JOIN) {
283 for (channel_t *cur = channels; cur; cur = cur->next) {
284 irc_channel_t *chan = (irc_channel_t*)cur;
285 if (cur->server != &srv->server || !chan->join)
287 net_print(&srv->net, "JOIN %s\n", chan->dest);
289 srv->state = IRC_READY;
293 static void irc_connect(irc_server_t *srv)
296 srv->net.recv = on_recv;
298 net_open(&srv->net, srv->host, srv->port);
302 srv->state = IRC_ENCRYPT;
304 srv->state = IRC_SEND_TLS;
310 for (server_t *cur = servers; cur; cur = cur->next) {
311 if (cur->protocol != IRC)
314 irc_server_t *srv = (irc_server_t*)cur;
316 srv->port = srv->tls ? 6697 : 6667;
318 srv->nick = strcopy(getenv("USER"));
320 srv->nick = strcopy("lameuser");
324 for (channel_t *cur = channels; cur; cur = cur->next) {
325 if (cur->server->protocol != IRC)
328 irc_channel_t *chan = (irc_channel_t*)cur;
330 chan->dest = strcopy(cur->name);
334 server_t *irc_server(void)
336 return new0(irc_server_t);
339 channel_t *irc_channel(void)
341 return new0(irc_channel_t);
344 void irc_config(server_t *server, channel_t *channel,
345 const char *group, const char *name,
346 const char *key, const char *value)
348 irc_server_t *srv = (irc_server_t*)server;
349 irc_channel_t *chan = (irc_channel_t*)channel;
352 if (match(key, "connect"))
353 srv->connect = get_bool(value);
354 else if (match(key, "host"))
355 srv->host = get_string(value);
356 else if (match(key, "port"))
357 srv->port = get_number(value);
358 else if (match(key, "tls"))
359 srv->tls = get_bool(value);
360 else if (match(key, "nick"))
361 srv->nick = get_string(value);
362 else if (match(key, "auth"))
363 srv->auth = get_string(value);
364 else if (match(key, "pass"))
365 srv->pass = get_string(value);
368 if (match(key, "dest"))
369 chan->dest = get_string(value);
370 else if (match(key, "join"))
371 chan->join = get_bool(value);
375 void irc_send(message_t *msg)
377 irc_channel_t *chan = (irc_channel_t*)msg->channel;
378 irc_server_t *srv = (irc_server_t*)msg->channel->server;
380 net_print(&srv->net, "PRIVMSG %s :%s\n", chan->channel, msg);