Blob


1 /* $Id: irc.c,v 1.2 2015/08/20 17:29:16 dhartmei Exp $ */
3 /*
4 * Copyright (c) 2023 Omar Polo <op@openbsd.org>
5 * Copyright (c) 2003-2004 Daniel Hartmeier
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
34 #include <err.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <string.h>
39 #include "irc.h"
40 #include "icb.h"
42 extern void scan(const char **, char *, size_t, const char *,
43 const char *);
44 extern void icbirc_quit(void);
45 extern int sync_write(int, const char *, int);
47 static void irc_cmd(char *, int, int);
49 static void irc_send_pong(int, const char *);
51 char irc_pass[256];
52 char irc_ident[256];
53 char irc_nick[256];
54 char irc_channel[256];
55 int in_irc_channel;
57 /*
58 * irc_recv() receives read(2) chunks and assembles complete lines, which are
59 * passed to irc_cmd(). Overlong lines are truncated after 65kB.
60 *
61 * XXX: argument checking is not as strong as for ICB (trusting the client)
62 *
63 */
65 void
66 irc_recv(const char *buf, unsigned len, int client_fd, int server_fd)
67 {
68 static char cmd[65535];
69 static unsigned off = 0;
71 while (len > 0) {
72 while (len > 0 && off < (sizeof(cmd) - 1) && *buf != '\n') {
73 cmd[off++] = *buf++;
74 len--;
75 }
76 if (off == (sizeof(cmd) - 1))
77 while (len > 0 && *buf != '\n') {
78 buf++;
79 len--;
80 }
81 /* off <= sizeof(cmd) - 1 */
82 if (len > 0 && *buf == '\n') {
83 buf++;
84 len--;
85 if (off > 0 && cmd[off - 1] == '\r')
86 cmd[off - 1] = 0;
87 else
88 cmd[off] = 0;
89 irc_cmd(cmd, client_fd, server_fd);
90 off = 0;
91 }
92 }
93 }
95 static void
96 irc_cmd(char *cmd, int client_fd, int server_fd)
97 {
98 if (!strncasecmp(cmd, "RAWICB ", 7)) {
99 icb_send_raw(server_fd, cmd + 7);
100 return;
103 char *argv[10], *p;
104 int argc = 1;
106 for (p = cmd, argv[0] = p; argc < 10 && (p = strchr(p, ' ')) != NULL;
107 argc++) {
108 *p = 0;
109 p++;
110 while (*p == ' ')
111 p++;
112 if (*p == ':') {
113 argv[argc] = p + 1;
114 argc++;
115 break;
117 argv[argc] = p;
120 if (!strcasecmp(argv[0], "PASS")) {
121 strlcpy(irc_pass, argv[1], sizeof(irc_pass));
122 } else if (!strcasecmp(argv[0], "USER")) {
123 strlcpy(irc_ident, argv[1], sizeof(irc_ident));
124 if (!icb_logged_in && irc_nick[0] && irc_ident[0])
125 icb_send_login(server_fd, irc_nick,
126 irc_ident, irc_pass);
127 } else if (!strcasecmp(argv[0], "NICK")) {
128 strlcpy(irc_nick, argv[1], sizeof(irc_nick));
129 if (icb_logged_in)
130 icb_send_name(server_fd, irc_nick);
131 else if (irc_nick[0] && irc_ident[0])
132 icb_send_login(server_fd, irc_nick,
133 irc_ident, irc_pass);
134 } else if (!strcasecmp(argv[0], "JOIN")) {
135 icb_send_group(server_fd,
136 argv[1] + (argv[1][0] == '#' ? 1 : 0));
137 } else if (!strcasecmp(argv[0], "PART")) {
138 in_irc_channel = 0;
139 } else if (!strcasecmp(argv[0], "PRIVMSG") ||
140 !strcasecmp(argv[0], "NOTICE")) {
141 char msg[8192];
142 unsigned i, j;
144 strlcpy(msg, argv[2], sizeof(msg));
145 /* strip \001 found in CTCP messages */
146 i = 0;
147 while (msg[i]) {
148 if (msg[i] == '\001') {
149 for (j = i; msg[j + 1]; ++j)
150 msg[j] = msg[j + 1];
151 msg[j] = 0;
152 } else
153 i++;
155 if (!strcmp(argv[1], irc_channel))
156 icb_send_openmsg(server_fd, msg);
157 else
158 icb_send_privmsg(server_fd, argv[1], msg);
159 } else if (!strcasecmp(argv[0], "MODE")) {
160 if (strcmp(argv[1], irc_channel))
161 return;
162 if (argc == 2)
163 icb_send_names(server_fd, irc_channel);
164 else {
165 if (strcmp(argv[2], "+o")) {
166 warnx("irc_cmd: invalid MODE args '%s'",
167 argv[2]);
168 return;
170 icb_send_pass(server_fd, argv[3]);
172 } else if (!strcasecmp(argv[0], "TOPIC")) {
173 if (strcmp(argv[1], irc_channel)) {
174 warnx("irc_cmd: invalid TOPIC channel '%s'",
175 argv[1]);
176 return;
178 icb_send_topic(server_fd, argv[2]);
179 } else if (!strcasecmp(argv[0], "LIST")) {
180 icb_send_list(server_fd);
181 } else if (!strcasecmp(argv[0], "NAMES")) {
182 icb_send_names(server_fd, argv[1]);
183 } else if (!strcasecmp(argv[0], "WHOIS")) {
184 icb_send_whois(server_fd, argv[1]);
185 } else if (!strcasecmp(argv[0], "WHO")) {
186 icb_send_who(server_fd, argv[1]);
187 } else if (!strcasecmp(argv[0], "KICK")) {
188 if (strcmp(argv[1], irc_channel)) {
189 warnx("irc_cmd: invalid KICK args '%s'", argv[1]);
190 return;
192 icb_send_boot(server_fd, argv[2]);
193 } else if (!strcasecmp(argv[0], "PING")) {
194 icb_send_noop(server_fd);
195 irc_send_pong(client_fd, argv[1]);
196 } else if (!strcasecmp(argv[0], "QUIT")) {
197 warnx("client QUIT");
198 icbirc_quit();
199 } else if (!strcasecmp(argv[0], "CAP")) {
200 if (!strcasecmp(argv[1], "LS")) {
201 printf("CAP * LS\n");
202 fflush(stdout);
203 } else if (!strcasecmp(argv[1], "REQ")) {
204 printf("CAP * NAK :%s\n", argv[2]);
205 fflush(stdout);
206 } else if (!strcasecmp(argv[1], "END"))
208 else
209 warnx("irc_cmd: unknown command 'CAP %s'", argv[1]);
210 } else
211 warnx("irc_cmd: unknown command '%s'", argv[0]);
214 void
215 irc_send_notice(int fd, const char *format, ...)
217 char cmd[8192], msg[8192];
218 va_list ap;
220 va_start(ap, format);
221 vsnprintf(msg, sizeof(msg), format, ap);
222 va_end(ap);
223 snprintf(cmd, sizeof(cmd), "NOTICE %s\r\n", msg);
224 sync_write(fd, cmd, strlen(cmd));
227 void
228 irc_send_code(int fd, const char *from, const char *nick, const char *code,
229 const char *format, ...)
231 char cmd[8192], msg[8192];
232 va_list ap;
234 va_start(ap, format);
235 vsnprintf(msg, sizeof(msg), format, ap);
236 va_end(ap);
237 snprintf(cmd, sizeof(cmd), ":%s %s %s :%s\r\n", from, code, nick, msg);
238 sync_write(fd, cmd, strlen(cmd));
241 void
242 irc_send_msg(int fd, const char *src, const char *dst, const char *msg)
244 char cmd[8192];
246 snprintf(cmd, sizeof(cmd), ":%s PRIVMSG %s :%s\r\n", src, dst, msg);
247 sync_write(fd, cmd, strlen(cmd));
250 void
251 irc_send_join(int fd, const char *src, const char *dst)
253 char cmd[8192];
255 snprintf(cmd, sizeof(cmd), ":%s JOIN :%s\r\n", src, dst);
256 sync_write(fd, cmd, strlen(cmd));
257 in_irc_channel = 1;
260 void
261 irc_send_part(int fd, const char *src, const char *dst)
263 char cmd[8192];
265 snprintf(cmd, sizeof(cmd), ":%s PART :%s\r\n", src, dst);
266 sync_write(fd, cmd, strlen(cmd));
269 void
270 irc_send_pong(int fd, const char *daemon)
272 char cmd[8192];
274 snprintf(cmd, sizeof(cmd), "PONG %s\r\n", daemon);
275 sync_write(fd, cmd, strlen(cmd));