1 /*	$Id: icb.c,v 1.3 2015/08/21 19:01:12 dhartmei Exp $ */
2 
3 /*
4  * Copyright (c) 2003-2004 Daniel Hartmeier
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *    - Redistributions of source code must retain the above copyright
12  *      notice, this list of conditions and the following disclaimer.
13  *    - Redistributions in binary form must reproduce the above
14  *      copyright notice, this list of conditions and the following
15  *      disclaimer in the documentation and/or other materials provided
16  *      with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32 
33 #if 0
34 static const char rcsid[] = "$Id: icb.c,v 1.3 2015/08/21 19:01:12 dhartmei Exp $";
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include "icb.h"
41 #include "irc.h"
42 
43 extern int	 sync_write(int, const char *, int);
44 
45 static unsigned char	 icb_args(const unsigned char *, unsigned char, char [255][255]);
46 static void		 icb_cmd(const unsigned char *, unsigned char, int, int);
47 static void		 icb_ico(int, const char *);
48 static void		 icb_iwl(int, const char *, const char *, long,
49 			    long, const char *, const char *);
50 static void		 icb_send_hw(int, const char *);
51 
52 extern int terminate_client;
53 int icb_logged_in = 0;
54 
55 static char icb_protolevel[256];
56 static char icb_hostid[256];
57 static char icb_serverid[256];
58 static char icb_moderator[256];
59 enum { imode_none, imode_list, imode_names, imode_whois, imode_who };
60 static int imode = imode_none;
61 static char icurgroup[256];
62 static char igroup[256];
63 static char inick[256];
64 static char ihostmask[256];
65 static unsigned off;
66 
67 /*
68  * A single ICB packet consists of a length byte, a command byte and
69  * variable arguments. The length includes command and arguments, but
70  * not the length byte itself. Since length is at most 255, the entire
71  * packet is at most 256 bytes long.
72  *
73  * icb_recv() gets passed read(2) chunks and assembles a complete packet
74  * (including the length byte) in cmd. Once complete, the packet is
75  * passed to icb_cmd() without the length byte. Hence, arguments to
76  * icb_cmd() are at most 255 bytes long.
77  *
78  * icb_cmd() skips the command byte and passes only the variable
79  * arguments to icb_args(). Hence, arguments to icb_args() are at most
80  * 254 octects long.
81  *
82  * Variable arguments consist of zero or more strings separated by
83  * \001 characters. The strings need not be null-terminated and may
84  * be empty. Hence, there can be at most 255 strings and a string can
85  * be at most 254 bytes long. icb_args() fills the array argument,
86  * null-terminating each argument.
87  *
88  * This (together with the comments below) should be convincing proof
89  * that the char [255][255] as well as the unsigned char variables
90  * cannot overflow.
91  *
92  * Further argument parsing in icb_cmd() and icb_ico() relies on the
93  * fact that any argument can be at most 255 bytes long (including
94  * null-termination).
95  *
96  * The icb_send_*() functions may get arbitrarily long arguments from
97  * IRC, they may generate packets of at most 256 bytes size. Overlong
98  * arguments are truncated, except for open and personal messages,
99  * which are split across multiple packets, if needed (generating
100  * separate messages on ICB).
101  *
102  * The ICB protocol definition is not very clear about null-termination
103  * of arguments for packets generated by the client. Without any
104  * termination, at least one common server implementation shows a
105  * buffer re-use bug. Terminating all arguments, however, causes
106  * another server implementation to refuse certain commands. The
107  * best approach seems to be to null-terminate only the last
108  * argument. Where the code below violates that rule, that was done
109  * intentionally after testing.
110  *
111  */
112 
113 void
scan(const unsigned char ** s,char * d,size_t siz,const char * skip,const char * term)114 scan(const unsigned char **s, char *d, size_t siz, const char *skip, const char *term)
115 {
116 	while (**s && strchr(skip, **s) != NULL)
117 		(*s)++;
118 	while (**s && strchr(term, **s) == NULL) {
119 		if (siz > 1) {
120 			*d++ = **s;
121 			siz--;
122 		}
123 		(*s)++;
124 	}
125 	if (siz > 0)
126 		*d = 0;
127 }
128 
129 void
icb_init(void)130 icb_init(void)
131 {
132 	memset(icb_protolevel, 0, sizeof(icb_protolevel));
133 	memset(icb_hostid, 0, sizeof(icb_hostid));
134 	memset(icb_serverid, 0, sizeof(icb_serverid));
135 	memset(icb_moderator, 0, sizeof(icb_moderator));
136 	imode = imode_none;
137 	memset(icurgroup, 0, sizeof(icurgroup));
138 	memset(igroup, 0, sizeof(igroup));
139 	memset(inick, 0, sizeof(inick));
140 	memset(ihostmask, 0, sizeof(ihostmask));
141 	off = 0;
142 }
143 
144 void
icb_recv(const char * buf,unsigned len,int fd,int server_fd)145 icb_recv(const char *buf, unsigned len, int fd, int server_fd)
146 {
147 	static unsigned char cmd[256];
148 
149 	while (len > 0) {
150 		if (off == 0) {
151 			cmd[off++] = *buf++;
152 			/* 0 < cmd[0] <= 255 */
153 			len--;
154 		}
155 		/* off > 0, 0 < cmd[0] <= 255 */
156 		while (len > 0 && (off - 1) < cmd[0]) {
157 			cmd[off++] = *buf++;
158 			len--;
159 		}
160 		/* len == 0 || (off - 1) == cmd[0] */
161 		if ((off - 1) == cmd[0]) {
162 			icb_cmd(cmd + 1, off - 1 /* <= 255 */, fd, server_fd);
163 			off = 0;
164 		}
165 	}
166 }
167 
168 static unsigned char
icb_args(const unsigned char * data,unsigned char len,char args[255][255])169 icb_args(const unsigned char *data, unsigned char len, char args[255][255])
170 {
171 	unsigned char i = 0, j = 0, k = 0;
172 
173 	/* 0 < len < 255 */
174 	while (i < len) {
175 		/* 0 <= i, j, k < 255 */
176 		if (data[i] == '\001') {
177 			args[j++][k] = 0;
178 			k = 0;
179 		} else if (data[i] == '\r' || data[i] == '\n')
180 			args[j][k++] = '?';
181 		else
182 			args[j][k++] = data[i];
183 		i++;
184 	}
185 	/* i, j, k < 255 */
186 	if (k > 0)
187 		args[j++][k] = 0;
188 	/* j <= 255 */
189 	for (i = j; i < 255; ++i)
190 		args[i][0] = 0;
191 	return (j);
192 }
193 
194 static void
icb_cmd(const unsigned char * cmd,unsigned char len,int fd,int server_fd)195 icb_cmd(const unsigned char *cmd, unsigned char len, int fd, int server_fd)
196 {
197 	char args[255][255];
198 	const unsigned char *a = (unsigned char *)args[1];
199 	unsigned char i, j;
200 	char s[8192];
201 
202 	if (len == 0)
203 		return;
204 
205 	/* 0 < len <= 255 */
206 	i = icb_args(cmd + 1, len - 1 /* < 255 */, args);
207 	/* 0 <= i <= 255 */
208 	switch (cmd[0]) {
209 	case 'a':	/* Login OK */
210 		irc_send_code(fd, icb_hostid, irc_nick, "001",
211 		    "Welcome to icbirc %s", irc_nick);
212 		irc_send_code(fd, icb_hostid, irc_nick, "002",
213 		    "Your host is %s running %s protocol %s",
214 		    icb_hostid, icb_serverid, icb_protolevel);
215 		/* some clients really want to see a MOTD */
216 		irc_send_code(fd, icb_hostid, irc_nick, "375",
217 		    "ICB server: %s", icb_serverid);
218 		irc_send_code(fd, icb_hostid, irc_nick, "376",
219 		    "End of MOTD");
220 		icb_logged_in = 1;
221 		break;
222 	case 'b':	/* Open Message */
223 		if (!in_irc_channel) {
224 			irc_send_join(fd, irc_nick, irc_channel);
225 			icb_send_names(server_fd, irc_channel);
226 		}
227 		irc_send_msg(fd, args[0], irc_channel, args[1]);
228 		break;
229 	case 'c':	/* Personal Message */
230 		irc_send_msg(fd, args[0], irc_nick, args[1]);
231 		break;
232 	case 'd':	/* Status Message */
233 		if (!strcmp(args[0], "Status") && !strncmp(args[1],
234 		    "You are now in group ", 21)) {
235 			if (irc_channel[0])
236 				irc_send_part(fd, irc_nick, irc_channel);
237 			irc_channel[0] = '#';
238 			a += 21;
239 			scan(&a, irc_channel + 1, sizeof(irc_channel) - 1,
240 			    " ", " ");
241 			irc_send_join(fd, irc_nick, irc_channel);
242 			icb_send_names(server_fd, irc_channel);
243 		} else if (!strcmp(args[0], "Arrive") ||
244 		    !strcmp(args[0], "Sign-on")) {
245 			char nick[256], host[256];
246 
247 			scan(&a, nick, sizeof(nick), " ", " ");
248 			scan(&a, host, sizeof(host), " (", ")");
249 			snprintf(s, sizeof(s), "%s!%s", nick, host);
250 			irc_send_join(fd, s, irc_channel);
251 		} else if (!strcmp(args[0], "Depart")) {
252 			char nick[256], host[256];
253 
254 			scan(&a, nick, sizeof(nick), " ", " ");
255 			scan(&a, host, sizeof(host), " (", ")");
256 			snprintf(s, sizeof(s), "%s!%s", nick, host);
257 			irc_send_part(fd, s, irc_channel);
258 		} else if (!strcmp(args[0], "Sign-off")) {
259 			char nick[256], host[256], reason[256];
260 
261 			scan(&a, nick, sizeof(nick), " ", " ");
262 			scan(&a, host, sizeof(host), " (", ")");
263 			scan(&a, reason, sizeof(reason), " )", "");
264 			if (strlen(reason) > 0 &&
265 			    reason[strlen(reason) - 1] == '.')
266 				reason[strlen(reason) - 1] = 0;
267 			snprintf(s, sizeof(s), ":%s!%s QUIT :%s\r\n",
268 			    nick, host, reason);
269 			sync_write(fd, s, strlen(s));
270 		} else if (!strcmp(args[0], "Name")) {
271 			char old_nick[256], new_nick[256];
272 
273 			scan(&a, old_nick, sizeof(old_nick), " ", " ");
274 			if (strncmp((const char *)a, " changed nickname to ", 21))
275 				return;
276 			a += 21;
277 			scan(&a, new_nick, sizeof(new_nick), " ", " ");
278 			snprintf(s, sizeof(s), ":%s NICK :%s\r\n",
279 			    old_nick, new_nick);
280 			sync_write(fd, s, strlen(s));
281 			if (!strcmp(old_nick, irc_nick))
282 				strlcpy(irc_nick, new_nick,
283 				    sizeof(irc_nick));
284 		} else if (!strcmp(args[0], "Topic")) {
285 			char nick[256], topic[256];
286 
287 			scan(&a, nick, sizeof(nick), " ", " ");
288 			if (strncmp((const char *)a, " changed the topic to \"", 23))
289 				return;
290 			a += 23;
291 			scan(&a, topic, sizeof(topic), "", "\"");
292 			snprintf(s, sizeof(s), ":%s TOPIC %s :%s\r\n",
293 			    nick, irc_channel, topic);
294 			sync_write(fd, s, strlen(s));
295 		} else if (!strcmp(args[0], "Pass")) {
296 			char old_mod[256], new_mod[256];
297 
298 			scan(&a, old_mod, sizeof(old_mod), " ", " ");
299 			if (!strncmp((const char *)a, " has passed moderation to ", 26)) {
300 				a += 26;
301 				scan(&a, new_mod, sizeof(new_mod), " ", " ");
302 				snprintf(s, sizeof(s),
303 				    ":%s MODE %s -o+o %s %s\r\n",
304 				    old_mod, irc_channel, old_mod, new_mod);
305 			} else if (!strcmp((const char *)a, " is now mod.")) {
306 				snprintf(s, sizeof(s),
307 				    ":%s MODE %s +o %s\r\n",
308 				    icb_hostid, irc_channel, old_mod);
309 			} else
310 				return;
311 			sync_write(fd, s, strlen(s));
312 			strlcpy(icb_moderator, new_mod, sizeof(icb_moderator));
313 		} else if (!strcmp(args[0], "Boot")) {
314 			char nick[256];
315 
316 			scan(&a, nick, sizeof(nick), " ", " ");
317 			if (strcmp((const char *)a, " was booted."))
318 				return;
319 			snprintf(s, sizeof(s), ":%s KICK %s %s :booted\r\n",
320 			    icb_moderator, irc_channel, nick);
321 			sync_write(fd, s, strlen(s));
322 		} else
323 			irc_send_notice(fd, "ICB Status Message: %s: %s",
324 			    args[0], args[1]);
325 		break;
326 	case 'e':	/* Error Message */
327 		irc_send_notice(fd, "ICB Error Message: %s", args[0]);
328 		break;
329 	case 'f':	/* Important Message */
330 		irc_send_notice(fd, "ICB Important Message: %s: %s",
331 		    args[0], args[1]);
332 		break;
333 	case 'g':	/* Exit */
334 		irc_send_notice(fd, "ICB Exit");
335 		printf("server Exit\n");
336 		terminate_client = 1;
337 		break;
338 	case 'i':	/* Command Output */
339 		if (!strcmp(args[0], "co")) {
340 			for (j = 1; j < i; ++j)
341 				icb_ico(fd, args[j]);
342 		} else if (!strcmp(args[0], "wl")) {
343 			icb_iwl(fd, args[1], args[2], atol(args[3]),
344 			    atol(args[5]), args[6], args[7]);
345 		} else if (!strcmp(args[0], "wh")) {
346 			/* display whois header, deprecated */
347 		} else
348 			irc_send_notice(fd, "ICB Command Output: %s: %u args",
349 			    args[0], i - 1);
350 		break;
351 	case 'j':	/* Protocol */
352 		strlcpy(icb_protolevel, args[0], sizeof(icb_protolevel));
353 		strlcpy(icb_hostid, args[1], sizeof(icb_hostid));
354 		strlcpy(icb_serverid, args[2], sizeof(icb_serverid));
355 		break;
356 	case 'k':	/* Beep */
357 		irc_send_notice(fd, "ICB Beep from %s", args[0]);
358 		break;
359 	case 'l':	/* Ping */
360 		irc_send_notice(fd, "ICB Ping '%s'", args[0]);
361 		break;
362 	case 'm':	/* Pong */
363 		irc_send_notice(fd, "ICB Pong '%s'", args[0]);
364 		break;
365 	case 'n':	/* No-op */
366 		irc_send_notice(fd, "ICB No-op");
367 		break;
368 	default:
369 		irc_send_notice(fd, "ICB unknown command %d: %u args",
370 		    (int)cmd[0], i);
371 	}
372 }
373 
374 static void
icb_iwl(int fd,const char * flags,const char * nick,long idle,long signon,const char * ident,const char * host)375 icb_iwl(int fd, const char *flags, const char *nick, long idle,
376     long signon, const char *ident, const char *host)
377 {
378 	char s[8192];
379 	int chanop = strchr(flags, 'm') != NULL;
380 
381 	if (imode == imode_whois && !strcmp(nick, inick)) {
382 		snprintf(s, sizeof(s), ":%s 311 %s %s %s %s * :\r\n",
383 		    icb_hostid, irc_nick, nick, ident, host);
384 		sync_write(fd, s, strlen(s));
385 		if (icurgroup[0]) {
386 			snprintf(s, sizeof(s), ":%s 319 %s %s :%s%s\r\n",
387 			    icb_hostid, irc_nick, nick, chanop ? "@" : "",
388 			    icurgroup);
389 			sync_write(fd, s, strlen(s));
390 		}
391 		snprintf(s, sizeof(s), ":%s 312 %s %s %s :\r\n",
392 		    icb_hostid, irc_nick, nick, icb_hostid);
393 		sync_write(fd, s, strlen(s));
394 		snprintf(s, sizeof(s), ":%s 317 %s %s %ld %ld :seconds idle, "
395 		    "signon time\r\n",
396 		    icb_hostid, irc_nick, nick, idle, signon);
397 		sync_write(fd, s, strlen(s));
398 		snprintf(s, sizeof(s), ":%s 318 %s %s :End of /WHOIS list.\r\n",
399 		    icb_hostid, irc_nick, nick);
400 		sync_write(fd, s, strlen(s));
401 	} else if (imode == imode_names && !strcmp(icurgroup, igroup)) {
402 		snprintf(s, sizeof(s), ":%s 353 %s @ %s :%s%s \r\n",
403 		    icb_hostid, irc_nick, icurgroup, chanop ? "@" : "", nick);
404 		sync_write(fd, s, strlen(s));
405 		snprintf(s, sizeof(s), ":%s 352 %s %s %s %s %s %s H :5 %s\r\n",
406 		    icb_hostid, irc_nick, icurgroup, nick, host, icb_hostid,
407 		    nick, ident);
408 		sync_write(fd, s, strlen(s));
409 	} else if (imode == imode_who) {
410 		int match;
411 
412 		if (ihostmask[0] == '#')
413 			match = !strcmp(icurgroup, ihostmask);
414 		else {
415 			char hostmask[1024];
416 
417 			snprintf(hostmask, sizeof(hostmask), "%s!%s@%s",
418 			    nick, ident, host);
419 			match = strstr(hostmask, ihostmask) != NULL;
420 		}
421 		if (match) {
422 			snprintf(s, sizeof(s), ":%s 352 %s %s %s %s %s %s "
423 			    "H :5 %s\r\n",
424 			    icb_hostid, irc_nick, icurgroup, nick, host,
425 			    icb_hostid, nick, ident);
426 			sync_write(fd, s, strlen(s));
427 		}
428 	}
429 
430 	if (chanop && !strcmp(icurgroup, irc_channel))
431 		strlcpy(icb_moderator, nick, sizeof(icb_moderator));
432 }
433 
434 static void
icb_ico(int fd,const char * arg)435 icb_ico(int fd, const char *arg)
436 {
437 	char s[8192];
438 
439 	if (!strncmp(arg, "Group: ", 7)) {
440 		char group[256];
441 		int i = 0;
442 		char *topic;
443 
444 		arg += 7;
445 		group[i++] = '#';
446 		while (*arg && *arg != ' ')
447 			group[i++] = *arg++;
448 		group[i] = 0;
449 		strlcpy(icurgroup, group, sizeof(icurgroup));
450 		topic = strstr(arg, "Topic: ");
451 		if (topic == NULL)
452 			topic = "(None)";
453 		else
454 			topic += 7;
455 		if (imode == imode_list) {
456 			snprintf(s, sizeof(s), ":%s 322 %s %s 1 :%s\r\n",
457 			    icb_hostid, irc_nick, group, topic);
458 			sync_write(fd, s, strlen(s));
459 		} else if (imode == imode_names &&
460 		    !strcmp(icurgroup, igroup)) {
461 			snprintf(s, sizeof(s), ":%s 332 %s %s :%s\r\n",
462 			    icb_hostid, irc_nick, icurgroup, topic);
463 			sync_write(fd, s, strlen(s));
464 		}
465 	} else if (!strncmp(arg, "Total: ", 7)) {
466 		if (imode == imode_list) {
467 			snprintf(s, sizeof(s), ":%s 323 %s :End of /LIST\r\n",
468 			    icb_hostid, irc_nick);
469 			sync_write(fd, s, strlen(s));
470 		} else if (imode == imode_names) {
471 			snprintf(s, sizeof(s), ":%s 366 %s %s :End of "
472 			    "/NAMES list.\r\n",
473 			    icb_hostid, irc_nick, igroup);
474 			sync_write(fd, s, strlen(s));
475 		} else if (imode == imode_who) {
476 			snprintf(s, sizeof(s), ":%s 315 %s %s :End of "
477 			    "/WHO list.\r\n",
478 			    icb_hostid, irc_nick, ihostmask);
479 			sync_write(fd, s, strlen(s));
480 		}
481 		imode = imode_none;
482 	} else if (strcmp(arg, " "))
483 		irc_send_notice(fd, "*** Unknown ico: %s", arg);
484 }
485 
486 #define MAX_MSG_SIZE 246
487 
488 void
icb_send_login(int fd,const char * nick,const char * ident,const char * group)489 icb_send_login(int fd, const char *nick, const char *ident, const char *group)
490 {
491 	char cmd[256];
492 	unsigned off = 1;
493 	const char *login_cmd = "login";
494 
495 	cmd[off++] = 'a';
496 	while (*ident && off < MAX_MSG_SIZE)
497 		cmd[off++] = *ident++;
498 	cmd[off++] = '\001';
499 	while (*nick && off < MAX_MSG_SIZE)
500 		cmd[off++] = *nick++;
501 	cmd[off++] = '\001';
502 	while (*group && off < MAX_MSG_SIZE)
503 		cmd[off++] = *group++;
504 	cmd[off++] = '\001';
505 	while (*login_cmd)
506 		cmd[off++] = *login_cmd++;
507 	cmd[off++] = '\001';
508 	cmd[off++] = '\001';
509 	cmd[off++] = '\001';
510 	cmd[0] = off - 1;
511 	sync_write(fd, cmd, off);
512 }
513 
514 void
icb_send_openmsg(int fd,const char * msg)515 icb_send_openmsg(int fd, const char *msg)
516 {
517 	unsigned char cmd[256];
518 	unsigned off;
519 
520 	while (*msg) {
521 		off = 1;
522 		cmd[off++] = 'b';
523 		while (*msg && off < MAX_MSG_SIZE)
524 			cmd[off++] = *msg++;
525 		cmd[off++] = 0;
526 		cmd[0] = off - 1;
527 		/* cmd[0] <= MAX_MSG_SIZE */
528 		sync_write(fd, (const char *)cmd, off);
529 	}
530 }
531 
532 void
icb_send_privmsg(int fd,const char * nick,const char * msg)533 icb_send_privmsg(int fd, const char *nick, const char *msg)
534 {
535 	unsigned char cmd[256];
536 	unsigned off;
537 
538 	while (*msg) {
539 		const char *n = nick;
540 
541 		off = 1;
542 		cmd[off++] = 'h';
543 		cmd[off++] = 'm';
544 		cmd[off++] = '\001';
545 		while (*n && off < MAX_MSG_SIZE)
546 			cmd[off++] = *n++;
547 		cmd[off++] = ' ';
548 		while (*msg && off < MAX_MSG_SIZE)
549 			cmd[off++] = *msg++;
550 		cmd[off++] = 0;
551 		cmd[0] = off - 1;
552 		/* cmd[0] <= MAX_MSG_SIZE */
553 		sync_write(fd, (const char *)cmd, off);
554 	}
555 }
556 
557 void
icb_send_group(int fd,const char * group)558 icb_send_group(int fd, const char *group)
559 {
560 	char cmd[256];
561 	unsigned off = 1;
562 
563 	cmd[off++] = 'h';
564 	cmd[off++] = 'g';
565 	cmd[off++] = '\001';
566 	while (*group && off < MAX_MSG_SIZE)
567 		cmd[off++] = *group++;
568 	cmd[off++] = 0;
569 	cmd[0] = off - 1;
570 	sync_write(fd, cmd, off);
571 }
572 
573 static void
icb_send_hw(int fd,const char * arg)574 icb_send_hw(int fd, const char *arg)
575 {
576 	char cmd[256];
577 	unsigned off = 1;
578 
579 	icurgroup[0] = 0;
580 	cmd[off++] = 'h';
581 	cmd[off++] = 'w';
582 	cmd[off++] = '\001';
583 	while (*arg && off < MAX_MSG_SIZE)
584 		cmd[off++] = *arg++;
585 	cmd[off++] = 0;
586 	cmd[0] = off - 1;
587 	sync_write(fd, cmd, off);
588 }
589 
590 void
icb_send_list(int fd)591 icb_send_list(int fd)
592 {
593 	if (imode != imode_none)
594 		return;
595 	imode = imode_list;
596 	icb_send_hw(fd, "-g");
597 }
598 
599 void
icb_send_names(int fd,const char * group)600 icb_send_names(int fd, const char *group)
601 {
602 	if (imode != imode_none)
603 		return;
604 	imode = imode_names;
605 	strlcpy(igroup, group, sizeof(igroup));
606 	icb_send_hw(fd, "");
607 }
608 
609 void
icb_send_whois(int fd,const char * nick)610 icb_send_whois(int fd, const char *nick)
611 {
612 	if (imode != imode_none)
613 		return;
614 	imode = imode_whois;
615 	strlcpy(inick, nick, sizeof(inick));
616 	icb_send_hw(fd, "");
617 }
618 
619 void
icb_send_who(int fd,const char * hostmask)620 icb_send_who(int fd, const char *hostmask)
621 {
622 	if (imode != imode_none)
623 		return;
624 	imode = imode_who;
625 	strlcpy(ihostmask, hostmask, sizeof(ihostmask));
626 	icb_send_hw(fd, "");
627 }
628 
629 void
icb_send_pass(int fd,const char * nick)630 icb_send_pass(int fd, const char *nick)
631 {
632 	char cmd[256];
633 	unsigned off = 1;
634 	const char *pass_cmd = "pass";
635 
636 	cmd[off++] = 'h';
637 	while (*pass_cmd)
638 		cmd[off++] = *pass_cmd++;
639 	cmd[off++] = '\001';
640 	while (*nick && off < MAX_MSG_SIZE)
641 		cmd[off++] = *nick++;
642 	cmd[off++] = 0;
643 	cmd[0] = off - 1;
644 	sync_write(fd, cmd, off);
645 }
646 
647 void
icb_send_topic(int fd,const char * topic)648 icb_send_topic(int fd, const char *topic)
649 {
650 	char cmd[256];
651 	unsigned off = 1;
652 	const char *topic_cmd = "topic";
653 
654 	cmd[off++] = 'h';
655 	while (*topic_cmd)
656 		cmd[off++] = *topic_cmd++;
657 	cmd[off++] = '\001';
658 	while (*topic && off < MAX_MSG_SIZE)
659 		cmd[off++] = *topic++;
660 	cmd[off++] = 0;
661 	cmd[0] = off - 1;
662 	sync_write(fd, cmd, off);
663 }
664 
665 void
icb_send_boot(int fd,const char * nick)666 icb_send_boot(int fd, const char *nick)
667 {
668 	char cmd[256];
669 	unsigned off = 1;
670 	const char *boot_cmd = "boot";
671 
672 	cmd[off++] = 'h';
673 	while (*boot_cmd)
674 		cmd[off++] = *boot_cmd++;
675 	cmd[off++] = '\001';
676 	while (*nick && off < MAX_MSG_SIZE)
677 		cmd[off++] = *nick++;
678 	cmd[off++] = 0;
679 	cmd[0] = off - 1;
680 	sync_write(fd, cmd, off);
681 }
682 
683 void
icb_send_name(int fd,const char * nick)684 icb_send_name(int fd, const char *nick)
685 {
686 	char cmd[256];
687 	unsigned off = 1;
688 	const char *name_cmd = "name";
689 
690 	cmd[off++] = 'h';
691 	while (*name_cmd)
692 		cmd[off++] = *name_cmd++;
693 	cmd[off++] = '\001';
694 	while (*nick && off < MAX_MSG_SIZE)
695 		cmd[off++] = *nick++;
696 	cmd[off++] = 0;
697 	cmd[0] = off - 1;
698 	sync_write(fd, cmd, off);
699 }
700 
701 void
icb_send_raw(int fd,const char * data)702 icb_send_raw(int fd, const char *data)
703 {
704 	char cmd[256];
705 	unsigned off = 1;
706 
707 	while (*data && off < MAX_MSG_SIZE) {
708 		if (*data == ',')
709 			cmd[off++] = '\001';
710 		else if (*data == '\\')
711 			cmd[off++] = 0;
712 		else
713 			cmd[off++] = *data;
714 		data++;
715 	}
716 	cmd[off++] = 0;
717 	cmd[0] = off - 1;
718 	sync_write(fd, cmd, off);
719 }
720 
721 void
icb_send_noop(int fd)722 icb_send_noop(int fd)
723 {
724 	char cmd[256];
725 	unsigned off = 1;
726 
727 	cmd[off++] = 'n';
728 	cmd[off++] = 0;
729 	cmd[0] = off - 1;
730 	sync_write(fd, cmd, off);
731 }
732