Blob


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