]> Pileus Git - ~andy/lamechat/blob - irc.c
Use net for IRC
[~andy/lamechat] / irc.c
1 /*
2  * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include "util.h"
25 #include "conf.h"
26 #include "chat.h"
27 #include "net.h"
28
29 /* IRC constants */
30 #define IRC_LINE 512
31
32 /* IRC types */
33 typedef enum {
34         IRC_DEAD,
35         IRC_NICK,
36         IRC_USER,
37         IRC_JOIN,
38         IRC_READY,
39 } irc_state_t;
40
41 typedef struct irc_server_t {
42         const char *name;
43         const char *protocol;
44         int         connect;
45         const char *host;
46         int         port;
47         const char *nick;
48         const char *auth;
49         const char *pass;
50
51         net_t       net;
52         irc_state_t state;
53         char        line[IRC_LINE];
54         int         pos;
55
56         struct irc_server_t *next;
57 } irc_server_t;
58
59 typedef struct irc_channel_t {
60         const char *name;
61         const char *channel;
62         const char *server;
63         int         join;
64         struct irc_channel_t *next;
65 } irc_channel_t;
66
67 /* Local data */
68 static irc_server_t  *servers;
69 static irc_channel_t *channels;
70
71 /* Local functions */
72 static irc_server_t *find_server(const char *name, int create)
73 {
74         irc_server_t *cur = NULL, *last = NULL;
75         for (cur = servers; cur; last = cur, cur = cur->next)
76                 if (match(cur->name, name))
77                         break;
78         if (!cur && create) {
79                 cur = new0(irc_server_t);
80                 cur->name = get_name(name);
81                 if (last)
82                         last->next = cur;
83                 else
84                         servers = cur;
85         }
86         return cur;
87 }
88
89 static irc_channel_t *find_channel(const char *name, int create)
90 {
91         irc_channel_t *cur = NULL, *last = NULL;
92         for (cur = channels; cur; last = cur, cur = cur->next)
93                 if (match(cur->name, name))
94                         break;
95         if (!cur && create) {
96                 cur = new0(irc_channel_t);
97                 cur->name = get_name(name);
98                 if (last)
99                         last->next = cur;
100                 else
101                         channels = cur;
102         }
103         return cur;
104 }
105
106 static void recv_line(irc_server_t *srv, const char *line)
107 {
108         static char src[IRC_LINE]; // (:([^ ]+) +)?   
109         static char cmd[IRC_LINE]; // (([A-Z0-9]+) +) 
110         static char dst[IRC_LINE]; // (([^ ]+)[= ]+)? 
111         static char arg[IRC_LINE]; // (([^: ]+) *)?   
112         static char msg[IRC_LINE]; // (:(.*))?        
113         static char from[IRC_LINE];
114
115         int i;
116         const char *c = line;
117
118         /* Clear strings */
119         src[0] = cmd[0] = dst[0] = arg[0] = msg[0] = from[0] = '\0';
120
121         /* Read src */
122         if (*c == ':') {
123                 c++;
124                 for (i = 0; *c && *c != ' '; i++)
125                         src[i] = *c++;
126                 while (*c == ' ')
127                         c++;
128                 src[i] = '\0';
129                 for (i = 0; src[i] && src[i] != '!' && src[i] != ' '; i++)
130                         from[i] = src[i];
131                 from[i] = '\0';
132         }
133
134         /* Read cmd */
135         for (i = 0; isalnum(*c); i++)
136                 cmd[i] = *c++;
137         while (*c == ' ')
138                 c++;
139         cmd[i] = '\0';
140
141         /* Read dst */
142         if ((*c && *c != ' ') &&
143             (strchr(c+1, ' ') || strchr(c+1, '='))) {
144                 for (i = 0; *c && *c != ' '; i++)
145                         dst[i] = *c++;
146                 while (*c == '=' || *c == ' ')
147                         c++;
148                 dst[i] = '\0';
149         }
150
151         /* Read arg */
152         if  (*c && *c != ':' && *c != ' ') {
153                 for (i = 0; *c && *c != ' '; i++)
154                         arg[i] = *c++;
155                 while (*c == ' ')
156                         c++;
157                 arg[i] = '\0';
158         }
159
160         /* Read msg */
161         if (*c == ':') {
162                 c++;
163                 for (i = 0; *c; i++)
164                         msg[i] = *c++;
165                 msg[i] = '\0';
166         }
167
168         debug("got line: %s", line);
169         //debug("  src %s", src);
170         //debug("  cmd %s", cmd);
171         //debug("  dst %s", dst);
172         //debug("  arg %s", arg);
173         //debug("  msg %s", msg);
174
175         /* Parse messages */
176         if (match(cmd, "001") || strstr(msg, "Welcome"))
177                 srv->state = IRC_JOIN;
178         if (match(cmd, "PING"))
179                 net_print(&srv->net, "PING %s\n", msg);
180         if (match(cmd, "PRIVMSG"))
181                 chat_recv(dst, from, msg);
182
183         /* Notices */
184         if (match(cmd, "NOTICE") ||
185             match(cmd, "001")    ||
186             match(cmd, "372")    ||
187             match(cmd, "375")    ||
188             match(cmd, "376"))
189                 chat_notice(from, NULL, "%s", msg);
190 }
191
192 static void on_recv(void *_srv, char *buf, int len)
193 {
194         irc_server_t *srv = _srv;
195
196         /* Handle Input */
197         for (int i = 0; i < len; i++) {
198                 if (srv->pos < IRC_LINE) {
199                         srv->line[srv->pos] = buf[i];
200                         srv->pos++;
201                 }
202                 if (buf[i] == '\n' || buf[i] == '\r') {
203                         srv->line[srv->pos-1] = '\0';
204                         if (srv->pos > 1)
205                                 recv_line(srv, srv->line);
206                         srv->pos = 0;
207                 }
208         }
209
210         /* State machine */
211         if (srv->state == IRC_USER) {
212                 if (net_print(&srv->net, "USER %s %s %s :%s\n",
213                                 getenv("USER") ?: "lameuser",
214                                 get_hostname(), srv->host, srv->nick))
215                         srv->state = IRC_NICK;
216         }
217         if (srv->state == IRC_NICK) {
218                 if (net_print(&srv->net, "NICK %s\n", srv->nick))
219                         srv->state = IRC_READY;
220         }
221         if (srv->state == IRC_JOIN) {
222                 for (irc_channel_t *chan = channels; chan; chan = chan->next)
223                         if (chan->join)
224                                 net_print(&srv->net, "JOIN %s\n", chan->channel);
225                 srv->state = IRC_READY;
226         }
227 }
228
229 static void irc_connect(irc_server_t *srv)
230 {
231         /* Net connect */
232         srv->net.recv = on_recv;
233         srv->net.data = srv;
234         net_open(&srv->net, srv->host, srv->port);
235
236         /* Setup server */
237         srv->state = IRC_USER;
238 }
239
240 /* IRC functions */
241 void irc_init(void)
242 {
243         for (irc_server_t *cur = servers; cur; cur = cur->next) {
244                 if (!match(cur->protocol, "irc"))
245                         continue;
246                 if (!cur->port)
247                         cur->port = 6667;
248                 if (!cur->nick)
249                         cur->nick = strcopy(getenv("USER"));
250                 if (!cur->nick)
251                         cur->nick = strcopy("lameuser");
252                 if (cur->connect)
253                         irc_connect(cur);
254         }
255 }
256
257 void irc_config(const char *group, const char *name, const char *key, const char *value)
258 {
259         irc_server_t  *srv;
260         irc_channel_t *chan;
261
262         if (match(group, "server")) {
263                 srv = find_server(name, 1);
264                 if (match(key, "protocol") &&
265                     match(value, "irc"))
266                         srv->protocol = get_string(value);
267                 if (match(srv->protocol, "irc")) {
268                         if (match(key, "connect"))
269                                 srv->connect = get_bool(value);
270                         else if (match(key, "host"))
271                                 srv->host = get_string(value);
272                         else if (match(key, "port"))
273                                 srv->port = get_number(value);
274                         else if (match(key, "nick"))
275                                 srv->nick = get_string(value);
276                         else if (match(key, "auth"))
277                                 srv->auth = get_string(value);
278                         else if (match(key, "pass"))
279                                 srv->pass = get_string(value);
280                 }
281         } else if (match(group, "channel")) {
282                 chan = find_channel(name, 1);
283                 if (match(key, "server") &&
284                     find_server(value, 0))
285                         chan->server = get_string(value);
286                 if (chan->server) {
287                         if (match(key, "channel"))
288                                 chan->channel = get_string(value);
289                         else if (match(key, "join"))
290                                 chan->join = get_bool(value);
291                 }
292         }
293 }
294
295 void irc_send(const char *channel, const char *msg)
296 {
297         irc_channel_t *chan = find_channel(channel, 0);
298         if (!chan)
299                 return;
300         irc_server_t *srv = find_server(chan->server, 0);
301         if (!srv)
302                 return;
303         net_print(&srv->net, "PRIVMSG %s :%s\n", chan->channel, msg);
304 }
305
306 void irc_exit(void)
307 {
308 }
309