1 /*
2  * icb.c - handles icb connections.
3  *
4  * written by matthew green
5  *
6  * copyright (C) 1995-2021.  Matthew R. Green.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS 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 CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "irc.h"
34 IRCII_RCSID("@(#)$eterna: icb.c,v 2.101 2021/02/27 08:00:42 mrg Exp $");
35 
36 /*
37  * ICB protocol is weird.  everything is separated by ASCII 001.  there
38  * are fortunately few protocol message types (unlike IRC).
39  *
40  * each message is formatted like:
41  *	CTdata
42  *
43  * where C is one byte interpreted as a message length, including this
44  * byte.  this makes the max length size 255.  the T is the packet type,
45  * making 256 types of messages available (not many are used).  and data
46  * is interpreted on a per-T basis.  normally, within T, there are
47  * different fields separtaed by ASCII 001.  most data types have a
48  * specific number of fields (perhaps variable).  see each function below
49  * for a description of how these work.
50  */
51 
52 /*
53 *** error: [ERROR] 104: Invalid packet type "P" (PRIVMSG blah :?DCC CHAT chat 3406786305 49527?)
54  */
55 
56 #include "ircaux.h"
57 #include "names.h"
58 #include "server.h"
59 #include "output.h"
60 #include "vars.h"
61 #include "ircterm.h"
62 #include "names.h"
63 #include "parse.h"
64 #include "screen.h"
65 #include "server.h"
66 #include "icb.h"
67 #include "hook.h"
68 #include "ignore.h"
69 #include "flood.h"
70 #include "whois.h"
71 #include "notice.h"
72 
73 /*
74  * private data for ICB.
75  */
76 struct icb_server_status {
77 	/*
78 	 * some grossness to prime the $chanusers() list.  if this variable
79 	 * is non-zero we are currently expecting Members: or more output.
80 	 */
81 	int	icb_got_group_waiting_for_members;
82 };
83 static	void	icb_close_callback(void **);
84 
85 /* sender functions */
86 static	void	icb_put_login(u_char *, u_char *, u_char *, u_char *, u_char *);
87 
88 /* hooks out of "icbcmd" */
89 static	void	icb_put_msg(u_char *);
90 static	void	icb_put_beep(u_char *);
91 static	void	icb_put_boot(u_char *);
92 static	void	icb_put_cancel(u_char *);
93 static	void	icb_put_echo(u_char *);
94 static	void	icb_put_pass(u_char *);
95 static	void	icb_put_status(u_char *);
96 static	void	icb_put_pong(u_char *);
97 static	void	icb_put_command(u_char *);
98 
99 /* receiver functions */
100 static	void	icb_got_login(u_char *);
101 static	void	icb_got_public(u_char *);
102 static	void	icb_got_msg(u_char *);
103 static	void	icb_got_status(u_char *);
104 static	void	icb_got_error(u_char *);
105 static	void	icb_got_important(u_char *);
106 static	void	icb_got_exit(u_char *);
107 static	void	icb_got_cmdout(u_char *);
108 static	void	icb_got_proto(u_char *);
109 static	void	icb_got_beep(u_char *);
110 static	void	icb_got_ping(u_char *);
111 static	void	icb_got_something(int, u_char *);
112 
113 /* misc. helper functions */
114 static	int	icb_split_msg(u_char *, u_char **, int);
115 static	void	icb_set_fromuserhost(u_char *);
116 static	void	icb_got_who(u_char *);
117 static	u_char	*icb_who_idle(u_char *);
118 static	u_char	*icb_who_signon(u_char *);
119 
120 /* icb state variables */
121 static	u_char	*icb_initial_status = UP("iml");
122 
123 static	int	icb_port_local = ICB_PORT;	/* port of icbd */
124 
125 static	void
icb_close_callback(void ** ptrptr)126 icb_close_callback(void **ptrptr)
127 {
128 	new_free(ptrptr);
129 }
130 
131 /*
132  * these are the functions that interpret incoming ICB packets.
133  */
134 
135 /*
136  * login packets fromt the server should be a single field "a"
137  */
138 static void
icb_got_login(u_char * line)139 icb_got_login(u_char *line)
140 {
141 	u_char	*server = server_get_name(parsing_server());
142 	struct icb_server_status *status;
143 
144 	status = new_malloc(sizeof *status);
145 	status->icb_got_group_waiting_for_members = 0;
146 
147 	server_set_version_string(parsing_server(), UP("ICB"));
148 	server_set_itsname(parsing_server(), server);
149 	server_set_server_private(parsing_server(), status, icb_close_callback);
150 	maybe_load_ircrc();
151 	update_all_status();
152 	do_hook(CONNECT_LIST, "%s %d", server, server_get_port(parsing_server()));
153 }
154 
155 /*
156  * public messages have two fields
157  *	sender
158  *	text
159  */
160 static void
icb_got_public(u_char * line)161 icb_got_public(u_char *line)
162 {
163 	u_char	*ap[ICB_GET_PUBLIC_MAXFIELD];
164 	int	ac, level;
165 	u_char	*high;
166 
167 	ac = icb_split_msg(line, ap, ICB_GET_PUBLIC_MAXFIELD);
168 	if (ac != ICB_GET_PUBLIC_MAXFIELD)
169 		return;
170 
171 	save_message_from();
172 	level = set_lastlog_msg_level(LOG_PUBLIC);
173 	message_from(server_get_icbgroup(parsing_server()), LOG_PUBLIC);
174 
175 	switch (double_ignore(ap[0], from_user_host(), IGNORE_PUBLIC))
176 	{
177 	case IGNORED:
178 		goto out;
179 	case HIGHLIGHTED:
180 		high = highlight_str();
181 		break;
182 	default:
183 		high = empty_string();
184 		break;
185 	}
186 
187 	if (check_flooding(ap[0], server_get_nickname(parsing_server()), PUBLIC_FLOOD, ap[1]))
188 	{
189 		if (do_hook(PUBLIC_LIST, "%s %s %s", ap[0],
190 		    server_get_icbgroup(parsing_server()), ap[1]))
191 			put_it("%s<%s>%s %s", high, ap[0], high, ap[1]);
192 		if (do_beep_on_level(LOG_PUBLIC))
193 			beep_em(1);
194 	}
195 out:
196 	set_lastlog_msg_level(level);
197 	restore_message_from();
198 }
199 
200 static void
icb_got_msg(u_char * line)201 icb_got_msg(u_char *line)
202 {
203 	u_char	*ap[ICB_GET_MSG_MAXFIELD];
204 	int	ac, level;
205 	u_char	*high;
206 
207 	ac = icb_split_msg(line, ap, ICB_GET_MSG_MAXFIELD);
208 	if (ac != ICB_GET_MSG_MAXFIELD)
209 		return;
210 	set_recv_nick(ap[0]);
211 	if (is_away_set())
212 		beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));
213 	save_message_from();
214 	message_from(ap[0], LOG_MSG);
215 	level = set_lastlog_msg_level(LOG_MSG);
216 
217 	switch (double_ignore(ap[0], from_user_host(), IGNORE_MSGS))
218 	{
219 	case IGNORED:
220 		goto out;
221 	case HIGHLIGHTED:
222 		high = highlight_str();
223 		break;
224 	default:
225 		high = empty_string();
226 		break;
227 	}
228 
229 	if (check_flooding(ap[0], server_get_nickname(parsing_server()), MSG_FLOOD, ap[1]))
230 	{
231 		if (do_hook(MSG_LIST, "%s %s", ap[0], ap[1]))
232 		{
233 			if (is_away_set())
234 			{
235 				time_t t;
236 				u_char *msg = NULL;
237 				size_t len = my_strlen(ap[1]) + 20;
238 
239 				t = time(NULL);
240 				msg = new_malloc(len);
241 				snprintf(CP(msg), len, "%s <%.16s>", ap[1], ctime(&t));
242 				put_it("%s*%s*%s %s", high, ap[0], high, msg);
243 				new_free(&msg);
244 			}
245 			else
246 				put_it("%s*%s*%s %s", high, ap[0], high, ap[1]);
247 		}
248 		if (do_beep_on_level(LOG_MSG))
249 			beep_em(1);
250 	}
251 out:
252 	set_lastlog_msg_level(level);
253 	restore_message_from();
254 }
255 
256 static const u_char status_match[] = "You are now in group ";
257 static const u_char signoff_match[] = "Your group moderator signed off.";
258 static const u_char change_match[] = "Group is now named ";
259 static const u_char change_match2[] = "renamed group to ";
260 static const u_char rsvp_match[] = "You are invited to group ";
261 static const u_char topic_match[] = "changed the topic to \"";
262 static const u_char nick_match[] = "changed nickname to ";
263 
264 static void
icb_got_status(u_char * line)265 icb_got_status(u_char *line)
266 {
267 	u_char	*ap[ICB_GET_STATUS_MAXFIELD], *group, *space, save;
268 	int	ac, do_say = 1, level;
269 
270 /* use these a few times */
271 #define	KILL_SPACE(what)						\
272 	do { 								\
273 		if ((space = my_index(what, ' ')) != NULL) {		\
274 			save = *space;					\
275 			*space = 0;					\
276 		}							\
277 	} while (0)
278 #define RESTORE_SPACE							\
279 	do {								\
280 		if (space)						\
281 			*space = save;					\
282 	} while (0)
283 
284 	save = 0;
285 	space = NULL;
286 	save_message_from();
287 	level = set_lastlog_msg_level(LOG_CRAP);
288 	ac = icb_split_msg(line, ap, ICB_GET_STATUS_MAXFIELD);
289 	if (ac != ICB_GET_STATUS_MAXFIELD)
290 		goto out;
291 	if (my_stricmp(ap[0], UP("status")) == 0)
292 	{
293 		if (my_strnicmp(ap[1], status_match, sizeof(status_match) - 1) == 0)
294 		{
295 			group = ap[1] + sizeof(status_match) - 1;
296 			KILL_SPACE(group);
297 			clear_channel_list(parsing_server());
298 			add_channel(group, 0, parsing_server(), CHAN_JOINED, 0);
299 			server_set_icbgroup(parsing_server(), group);
300 			icb_set_fromuserhost(empty_string());
301 			message_from(group, LOG_CRAP);
302 			if (do_hook(JOIN_LIST, "%s %s %s", server_get_nickname(parsing_server()), group, empty_string()) == 0)
303 				do_say = 0;
304 			if (get_int_var(SHOW_CHANNEL_NAMES_VAR))
305 				icb_put_funny_stuff(UP("NAMES"), group, NULL);
306 			RESTORE_SPACE;
307 		}
308 		/* leave do_say set */
309 	}
310 	else
311 	if (my_stricmp(ap[0], UP("sign-on")) == 0)
312 	{
313 		KILL_SPACE(ap[1]);
314 		icb_set_fromuserhost(space+1);
315 		message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
316 		if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
317 		    do_hook(JOIN_LIST, "%s %s %s", ap[1], server_get_icbgroup(parsing_server()), empty_string()) == 0)
318 			do_say = 0;
319 		add_to_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server(), 0, 0);
320 		RESTORE_SPACE;
321 	}
322 	else
323 	if (my_stricmp(ap[0], UP("sign-off")) == 0)
324 	{
325 		if (my_strnicmp(ap[1], signoff_match, sizeof(signoff_match) - 1) != 0)
326 		{
327 			u_char *s = server_get_icbgroup(parsing_server());
328 
329 			KILL_SPACE(ap[1]);
330 			icb_set_fromuserhost(space+1);
331 			message_from(s, LOG_CRAP);
332 			if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
333 			    (do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", s, ap[1], s) == 0 ||
334 			     do_hook(SIGNOFF_LIST, "%s %s", ap[1], s) == 0))
335 				do_say = 0;
336 			remove_from_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server());
337 			RESTORE_SPACE;
338 		}
339 		/* leave do_say set */
340 	}
341 	else
342 	if (my_stricmp(ap[0], UP("arrive")) == 0)
343 	{
344 		KILL_SPACE(ap[1]);
345 		icb_set_fromuserhost(space+1);
346 		message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
347 		if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
348 		    do_hook(JOIN_LIST, "%s %s %s", ap[1], server_get_icbgroup(parsing_server()), empty_string()) == 0)
349 			do_say = 0;
350 		add_to_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server(), 0, 0);
351 		RESTORE_SPACE;
352 	}
353 	else
354 	if (my_stricmp(ap[0], UP("depart")) == 0)
355 	{
356 		KILL_SPACE(ap[1]);
357 		icb_set_fromuserhost(space+1);
358 		message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
359 		if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
360 		    do_hook(LEAVE_LIST, "%s %s", ap[1], server_get_icbgroup(parsing_server())) == 0)
361 			do_say = 0;
362 		remove_from_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server());
363 		RESTORE_SPACE;
364 	}
365 	else
366 	if (my_stricmp(ap[0], UP("change")) == 0)
367 	{
368 		int	match2;
369 		int	len;
370 
371 		group = 0;
372 		KILL_SPACE(ap[1]);
373 		match2 = my_strnicmp(space+1, change_match2, sizeof(change_match2) - 1);
374 		RESTORE_SPACE;
375 		if (match2 == 0)
376 			group = space+1 + sizeof(change_match2) - 1;
377 		else
378 		if (my_strnicmp(ap[1], change_match, sizeof(change_match) - 1) == 0)
379 			group = ap[1] + sizeof(change_match) - 1;
380 
381 		if (group)
382 		{
383 			len = my_strlen(group);
384 			if (group[len - 1] == '.')
385 				group[len - 1] = 0;	/* kill the period */
386 
387 			message_from(group, LOG_CRAP);
388 			rename_channel(server_get_icbgroup(parsing_server()), group, parsing_server());
389 			server_set_icbgroup(parsing_server(), group);
390 			icb_set_fromuserhost(empty_string());
391 		}
392 		/* leave do_say set for all cases */
393 	}
394 	else
395 	if (my_stricmp(ap[0], UP("RSVP")) == 0)
396 	{
397 		if (my_strnicmp(ap[1], rsvp_match, sizeof(rsvp_match) - 1) == 0)
398 		{
399 			group = ap[1] + sizeof(rsvp_match) - 1;
400 			KILL_SPACE(group);
401 			set_invite_channel(group);
402 			RESTORE_SPACE;
403 		}
404 		/* leave do_say set */
405 	}
406 	else
407 	if (my_stricmp(ap[0], UP("topic")) == 0)
408 	{
409 		KILL_SPACE(ap[1]);
410 		if (my_strnicmp(space + 1, topic_match, sizeof(topic_match) - 1) == 0)
411 		{
412 			u_char	*topic, *lbuf = NULL;
413 
414 			group = server_get_icbgroup(parsing_server());
415 			message_from(group, LOG_CRAP);
416 			topic = space + 1 + sizeof(topic_match) - 1;
417 			/* trim the final " */
418 			malloc_strcpy(&lbuf, topic);
419 			lbuf[my_strlen(lbuf) - 1] = '\0';
420 			if (do_hook(TOPIC_LIST, "%s %s %s", ap[1], group, lbuf) == 0)
421 				do_say = 0;
422 			new_free(&lbuf);
423 		}
424 		RESTORE_SPACE;
425 		/* leave do_say set */
426 	}
427 	else
428 	if (my_stricmp(ap[0], UP("name")) == 0)
429 	{
430 		KILL_SPACE(ap[1]);
431 		if (my_strnicmp(space + 1, nick_match, sizeof(nick_match) - 1) == 0)
432 		{
433 			u_char	*new_nick;
434 
435 			group = server_get_icbgroup(parsing_server());
436 			message_from(group, LOG_CRAP);
437 			new_nick = space + 1 + sizeof(nick_match) - 1;
438 			if (do_hook(CHANNEL_NICK_LIST, "%s %s %s", group, ap[1], new_nick) &&
439 			    do_hook(NICKNAME_LIST, "%s %s", ap[1], new_nick) == 0)
440 				do_say = 0;
441 			rename_nick(ap[1], new_nick, parsing_server());
442 		}
443 		RESTORE_SPACE;
444 		/* leave do_say set */
445 	}
446 	/* make default info messages go to the current channel */
447 	else
448 		message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
449 
450 #if 0
451 /* these need to be match for /ignore to work */
452 *** info Sign-on: nick (user@domain.com) entered group
453 *** info Sign-off: nick (user@domain.com) has signed off.
454 #endif
455 
456 	if (do_say && do_hook(ICB_STATUS_LIST, "%s %s", ap[0], ap[1]))
457 		say("info %s: %s", ap[0], ap[1]);
458 out:
459 	set_lastlog_msg_level(level);
460 	restore_message_from();
461 }
462 
463 static void
icb_set_fromuserhost(u_char * what)464 icb_set_fromuserhost(u_char *what)
465 {
466 	static	u_char	*icb_fromuserhost = NULL;
467 	u_char	*righty;
468 
469 	if (!what || !*what)
470 		what = empty_string();
471 	else if (*what == '(')
472 		what++;
473 	malloc_strcpy(&icb_fromuserhost, what);	/* ( for below */
474 	if ((righty = my_index(icb_fromuserhost, ')')))
475 		*righty = 0;
476 	set_from_user_host(icb_fromuserhost);
477 }
478 
479 static void
icb_got_error(u_char * line)480 icb_got_error(u_char *line)
481 {
482 	int	level;
483 
484 	save_message_from();
485 	level = set_lastlog_msg_level(LOG_CRAP);
486 	message_from(NULL, LOG_CRAP);
487 	if (do_hook(ICB_ERROR_LIST, "%s", line))
488 		say("error: %s", line);
489 	set_lastlog_msg_level(level);
490 	restore_message_from();
491 }
492 
493 static void
icb_got_important(u_char * line)494 icb_got_important(u_char *line)
495 {
496 	u_char	*ap[ICB_GET_IMPORTANT_MAXFIELD];
497 	int	ac, level;
498 
499 	ac = icb_split_msg(line, ap, ICB_GET_IMPORTANT_MAXFIELD);
500 	if (ac != ICB_GET_IMPORTANT_MAXFIELD)
501 		return;
502 	save_message_from();
503 	level = set_lastlog_msg_level(LOG_CRAP);
504 	message_from(NULL, LOG_CRAP);
505 	if (do_hook(SERVER_NOTICE_LIST, "%s *** %s", ap[0], ap[1]))
506 		say("Important Message %s: %s", ap[0], ap[1]);
507 	set_lastlog_msg_level(level);
508 	restore_message_from();
509 }
510 
511 static void
icb_got_exit(u_char * line)512 icb_got_exit(u_char *line)
513 {
514 	int	level;
515 
516 	level = set_lastlog_msg_level(LOG_CRAP);
517 	say("ICB server disconnecting us");
518 	close_server(parsing_server(), empty_string());
519 	set_lastlog_msg_level(level);
520 }
521 
522 /*
523  * command output: we split this up a little ...
524  */
525 
526 static	u_char	*
icb_who_idle(u_char * line)527 icb_who_idle(u_char *line)
528 {
529 	time_t	idle = (time_t)my_atoi(line);
530 	static	u_char	lbuf[16];
531 
532 	if (idle > 99 * 60)
533 		snprintf(CP(lbuf), sizeof lbuf, "%5dm", (int)idle / 60);
534 	else
535 	if (idle < 60)
536 		snprintf(CP(lbuf), sizeof lbuf, "%d sec", (int)idle);
537 	else
538 		snprintf(CP(lbuf), sizeof lbuf, "%d:%02ds", (int)idle / 60, (int)idle % 60);
539 	/* only has 6 chars */
540 	lbuf[6] = 0;
541 	return (lbuf);
542 }
543 
544 static	u_char	*
icb_who_signon(u_char * line)545 icb_who_signon(u_char *line)
546 {
547 	time_t	their_time = (time_t)my_atoi(line);
548 	u_char	*s = UP(asctime(localtime(&their_time)));
549 
550 	s[16] = '\0';
551 	/* Tue Mar  2 05:10:10 1999J */
552 	/*     <---------->          */
553 	return (s + 4);
554 }
555 
556 static void
icb_got_who(u_char * line)557 icb_got_who(u_char *line)
558 {
559 	u_char	*ap[ICB_GET_WHOOUT_MAXFIELD], *arg0;
560 	int	ac, level;
561 
562 	/* just get the command */
563 	ac = icb_split_msg(line, ap, ICB_GET_WHOOUT_MAXFIELD);
564 	if (ac != ICB_GET_WHOOUT_MAXFIELD)
565 	{
566 		yell("--- icb_got_who: split ac(%d) not 8", ac);
567 		return;
568 	}
569 	/* ap[3] is always "0" */
570 	/* these are: mod?, nick, idle, signon, user, [@]host, status? */
571 	/* keep format in sync with below */
572 	save_message_from();
573 	level = set_lastlog_msg_level(LOG_CRAP);
574 	message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
575 	arg0 = UP(ap[0][0] == 'm' ? "*" : " ");
576 	if (do_hook(ICB_WHO_LIST, "%s%s %s %s %s %s %s", arg0,
577 				ap[1], ap[2], ap[4],
578 				ap[5], ap[6], ap[7]))
579 		say("%s%-13s %6s %-12s %s@%s %s", arg0, ap[1],
580 				icb_who_idle(ap[2]),
581 				icb_who_signon(ap[4]),
582 				ap[5], ap[6], ap[7]);
583 	set_lastlog_msg_level(level);
584 	restore_message_from();
585 }
586 
587 static const u_char group_match[] = "Group: ";
588 static const u_char members_match[] = "    Members: ";
589 
590 static void icb_cmdout_check_members(u_char *);
591 static void
icb_cmdout_check_members(u_char * line)592 icb_cmdout_check_members(u_char *line)
593 {
594 	u_char	*group, *nick_list = NULL;
595 	struct icb_server_status *status;
596 
597 	status = server_get_server_private(parsing_server());
598 
599 	if (status->icb_got_group_waiting_for_members)
600 		status->icb_got_group_waiting_for_members++;
601 	else if (my_strnicmp(line, UP(members_match),
602 	    sizeof(members_match) - 1) == 0)
603 	{
604 		status->icb_got_group_waiting_for_members = 2;
605 		line += sizeof(members_match) - 1;
606 	}
607 	else
608 		return;
609 
610 	/* parse list of nicks, calling add_to_channel() */
611 	group = server_get_icbgroup(parsing_server());
612 	malloc_strcpy(&nick_list, line);
613 	line = nick_list;
614 	while (line && *line)
615 	{
616 		u_char	*next;
617 
618 		next = my_index(line, ',');
619 		if (next)
620 		{
621 			*next++ = '\0';
622 			if (*next == ' ')
623 				next++;
624 		}
625 		add_to_channel(group, line, parsing_server(), 0, 0);
626 		line = next;
627 	}
628 	new_free(&nick_list);
629 }
630 
631 static void
icb_got_cmdout(u_char * line)632 icb_got_cmdout(u_char *line)
633 {
634 	u_char	*ap[2];
635 	int	ac, level;
636 
637 	/* just get the command */
638 	ac = icb_split_msg(line, ap, -2);
639 
640 	save_message_from();
641 	level = set_lastlog_msg_level(LOG_CRAP);
642 	message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
643 	if (ac == 2)
644 	{
645 		if (my_stricmp(ap[0], UP("co")) == 0)
646 		{
647 			if (my_strnicmp(ap[1], UP(group_match),
648 			    sizeof(group_match) - 1) == 0) {
649 				u_char *s = ap[1] + sizeof(group_match) - 1;
650 
651 				/* skip "*name*<spaces>" */
652 				while (!isspace(*s))
653 					s++;
654 				while (isspace(*s))
655 					s++;
656 				if (*s == '(' && *(s+4) == ')') {
657 					u_char mode[4];
658 
659 					mode[0] = s[1];
660 					mode[1] = s[2];
661 					mode[2] = s[3];
662 					mode[3] = 0;
663 					server_set_icbmode(parsing_server(), mode);
664 				}
665 			}
666 			else
667 				icb_cmdout_check_members(ap[1]);
668 
669 			if (do_hook(ICB_CMDOUT_LIST, "%s", ap[1]))
670 				say("%s", ap[1]);
671 		}
672 		else
673 		/* who list */
674 		if (my_stricmp(ap[0], UP("wl")) == 0)
675 			icb_got_who(ap[1]);
676 		else
677 			put_it("[unknown command output %s] %s", ap[0], ap[1]);
678 	}
679 	else
680 	/* head of who list */
681 	if (my_stricmp(ap[0], UP("wh")) == 0 &&
682 	    do_hook(ICB_WHO_LIST, "%s %s %s %s %s %s",
683 			"Nickname", "Idle", "Sign-On", "Account",
684 			empty_string(), empty_string()))
685 		/* keep format in sync with above. */
686 		say(" %-13s %6s %-12s %s", "Nickname", "Idle",
687 			"Sign-On", "Account");
688 	set_lastlog_msg_level(level);
689 	restore_message_from();
690 }
691 
692 /*
693  * we send our login to the server now... it has told us we are
694  * actually there, time to login.
695  */
696 static void
icb_got_proto(u_char * line)697 icb_got_proto(u_char *line)
698 {
699 	int	server = parsing_server();
700 	u_char	*chan = server_get_icbgroup(server);
701 	u_char	*mode = server_get_icbmode(server);
702 
703 	if (!chan)
704 		chan = empty_string();
705 	if (!mode || !*mode)
706 		mode = icb_initial_status;
707 	say("You are wasting time.");
708 	server_is_connected(server, 1);
709 	icb_put_login(my_username(),
710 		server_get_nickname(server),
711 		chan,
712 		server_get_password(server),
713 		mode);
714 	do_hook(CONNECT_LIST, "%s %d",
715 	    server_get_name(server), server_get_port(server));
716 }
717 
718 /*
719  * if we beep, we beep.  tell the user about the annoy packet
720  * anyway.
721  */
722 static void
icb_got_beep(u_char * line)723 icb_got_beep(u_char *line)
724 {
725 	int	level;
726 
727 	if (get_int_var(BEEP_VAR))
728 		term_beep();
729 	save_message_from();
730 	level = set_lastlog_msg_level(LOG_CRAP);
731 	message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
732 	say("%s wants to annoy you.", line);
733 	set_lastlog_msg_level(level);
734 	restore_message_from();
735 }
736 
737 /*
738  * if we get a ping, send a pong
739  */
740 static void
icb_got_ping(u_char * line)741 icb_got_ping(u_char *line)
742 {
743 	icb_put_pong(line);
744 }
745 
746 /*
747  * eek.
748  */
749 static void
icb_got_something(int type,u_char * line)750 icb_got_something(int type, u_char *line)
751 {
752 	say("unknown: packet type %d, ignored", type);
753 }
754 
755 /*
756  * below are functions to send messages to the icb server.
757  */
758 
759 /*
760  * for hooks perhaps we should have a "icb <command> <args>"
761  * hook, that does 'everything'.  dunno, there are enough
762  * irc-like ones to afford just adding a few icb specifics..
763  */
764 
765 /*
766  * login packets have from 5 to 7 fields:
767  *	username
768  *	nickname
769  *	default group
770  *	login command
771  *	password
772  *	default group status (optional)
773  *	protocol level (optional, deprecated).
774  *
775  * the login command must be either "login" or "w", to either login
776  * to ICB or see who is on.
777  */
778 static void
icb_put_login(u_char * user,u_char * nick,u_char * group,u_char * pass,u_char * status)779 icb_put_login(u_char *user, u_char *nick, u_char *group, u_char *pass, u_char *status)
780 {
781 	u_char *mode, *prefix;
782 
783 	mode = server_get_icbmode(parsing_server());
784 	if (my_strcmp(group, "..") == 0 &&
785 	    mode && my_strlen(mode) > 1 && mode[1] == 'i') {
786 		/* save previous status */
787 		prefix = UP("..");
788 	} else
789 		prefix = UP("");
790 
791 	send_to_server("%c%s%c%s%c%s%s%c%s%c%s%c%s", ICB_LOGIN,
792 		user, ICB_SEP,
793 		nick, ICB_SEP,
794 		prefix, group, ICB_SEP,
795 		"login", ICB_SEP,
796 		pass ? pass : (u_char *) "", ICB_SEP,
797 		status);
798 }
799 
800 /*
801  * public packets have 1 field:
802  *	text
803  */
804 void
icb_put_public(u_char * line)805 icb_put_public(u_char *line)
806 {
807 	int	level;
808 	size_t	len, remain;
809 	const	int	from_server = get_from_server();
810 
811 	if (get_display())
812 	{
813 		save_message_from();
814 		level = set_lastlog_msg_level(LOG_PUBLIC);
815 		message_from(server_get_icbgroup(from_server), LOG_PUBLIC);
816 		if (do_hook(SEND_PUBLIC_LIST, "%s %s", server_get_icbgroup(from_server), line))
817 			put_it("> %s", line);
818 		set_lastlog_msg_level(level);
819 		restore_message_from();
820 	}
821 
822 	/*
823 	 * deal with ICB 255 length limits.  we have 255 bytes
824 	 * maximum to deal with.  as public messages are send
825 	 * out with our nickname, we must take away that much,
826 	 * one for the space after our nick, one for the public
827 	 * message token, one for the trailing nul, one for the
828 	 * ^A, and one more for good measure, totalling 5.
829 	 */
830 	remain = 250 - my_strlen(server_get_nickname(from_server));
831 	while (*line) {
832 		u_char	b[256], *s;
833 		size_t copylen;
834 
835 		len = my_strlen(line);
836 		copylen = remain;
837 		if (len > remain)
838 		{
839 			int i;
840 
841 			/*
842 			 * try to find a space in the previous
843 			 * 10 characters, and split there instead.
844 			 */
845 			for (i = 1; i < 11 && i < len; i++)
846 				if (isspace(line[remain - i]))
847 				{
848 					copylen -= i;
849 					break;
850 				}
851 			my_strncpy(b, line, copylen);
852 			b[copylen] = 0;
853 			s = b;
854 		}
855 		else
856 			s = line;
857 		send_to_server("%c%s", ICB_PUBLIC, s);
858 		line += len > copylen ? copylen : len;
859 	}
860 }
861 
862 /*
863  * msg packets have 2 fields:
864  *	to		- recipient of message
865  *	text
866  */
867 static void
icb_put_msg(u_char * line)868 icb_put_msg(u_char *line)
869 {
870 	u_char	*to;
871 
872 	if ((to = next_arg(line, &line)) == NULL)
873 		line = empty_string();
874 	icb_put_msg2(to, line);
875 }
876 
877 void
icb_put_msg2(u_char * to,u_char * line)878 icb_put_msg2(u_char *to, u_char *line)
879 {
880 	int	level;
881 	size_t	nlen, mlen, len, remain;
882 
883 	if (get_display())
884 	{
885 		save_message_from();
886 		level = set_lastlog_msg_level(LOG_MSG);
887 		message_from(to, LOG_MSG);
888 		if (do_hook(SEND_MSG_LIST, "%s %s", to, line))
889 			put_it("-> *%s* %s", to, line);
890 		set_lastlog_msg_level(level);
891 		restore_message_from();
892 	}
893 
894 	/*
895 	 * deal with ICB 255 length limits.  we have 255 bytes maximum to
896 	 * deal with.  as private messages are send out with our nickname,
897 	 * but are sent in with the target's nickname, we must take away
898 	 * the larger length of these, plus one for the space after our
899 	 * nick, one for the command tag, one for the private message
900 	 * token, one for the separator, one for the trailing nul, one
901 	 * for the ^A, and one more for good measure, totalling 7.
902 	 */
903 	nlen = my_strlen(to);
904 	mlen = my_strlen(server_get_nickname(get_from_server()));
905 	if (nlen > mlen)
906 		remain = 248 - nlen;
907 	else
908 		remain = 248 - mlen;
909 	while (*line)
910 	{
911 		u_char	b[256], *s;
912 
913 		len = my_strlen(line);
914 		if (len > remain)
915 		{
916 			my_strncpy(b, line, remain);
917 			b[remain] = 0;
918 			s = b;
919 		}
920 		else
921 			s = line;
922 		send_to_server("%cm%c%s %s", ICB_COMMAND, ICB_SEP, to, s);
923 		line += len > remain ? remain : len;
924 	}
925 }
926 
927 static	void
icb_put_beep(u_char * line)928 icb_put_beep(u_char *line)
929 {
930 	send_to_server("%cbeep%c%s", ICB_COMMAND, ICB_SEP, line);
931 }
932 
933 static	void
icb_put_pong(u_char * line)934 icb_put_pong(u_char *line)
935 {
936 	send_to_server("%c", ICB_PONG);
937 }
938 
939 static	void
icb_put_boot(u_char * line)940 icb_put_boot(u_char *line)
941 {
942 	send_to_server("%cboot%c%s", ICB_COMMAND, ICB_SEP, line);
943 }
944 
945 static	void
icb_put_cancel(u_char * line)946 icb_put_cancel(u_char *line)
947 {
948 	send_to_server("%ccancel%c%s", ICB_COMMAND, ICB_SEP, line);
949 }
950 
951 static	void
icb_put_echo(u_char * line)952 icb_put_echo(u_char *line)
953 {
954 	send_to_server("%cechoback%c%s", ICB_COMMAND, ICB_SEP, line);
955 }
956 
957 void
icb_put_group(u_char * line)958 icb_put_group(u_char *line)
959 {
960 	send_to_server("%cg%c%s", ICB_COMMAND, ICB_SEP, line);
961 }
962 
963 void
icb_put_invite(u_char * line)964 icb_put_invite(u_char *line)
965 {
966 	send_to_server("%cinvite%c%s", ICB_COMMAND, ICB_SEP, line);
967 }
968 
969 void
icb_put_motd(u_char * line)970 icb_put_motd(u_char *line)
971 {
972 	send_to_server("%cmotd%c", ICB_COMMAND, ICB_SEP);
973 }
974 
975 void
icb_put_nick(u_char * line)976 icb_put_nick(u_char *line)
977 {
978 	send_to_server("%cname%c%s", ICB_COMMAND, ICB_SEP, line);
979 	server_set_nickname(get_window_server(0), line);
980 }
981 
982 static	void
icb_put_pass(u_char * line)983 icb_put_pass(u_char *line)
984 {
985 	send_to_server("%cpass%c%s", ICB_COMMAND, ICB_SEP, line);
986 }
987 
988 static	void
icb_put_status(u_char * line)989 icb_put_status(u_char *line)
990 {
991 	send_to_server("%cstatus%c%s", ICB_COMMAND, ICB_SEP, line);
992 }
993 
994 void
icb_put_topic(u_char * line)995 icb_put_topic(u_char *line)
996 {
997 	send_to_server("%ctopic%c%s", ICB_COMMAND, ICB_SEP, line);
998 }
999 
1000 void
icb_put_version(u_char * line)1001 icb_put_version(u_char *line)
1002 {
1003 	send_to_server("%cv%c", ICB_COMMAND, ICB_SEP);
1004 }
1005 
1006 /*
1007  * /names & /list support:
1008  *
1009  * a bare /names is ICB /who -s
1010  * a bare /list is ICB /who -g
1011  * any arguments are just passed, though a "*" (ircII "this channel") is
1012  * converted into a "." (icb "this channel").
1013  */
1014 void
icb_put_funny_stuff(u_char * command,u_char * args,u_char * subargs)1015 icb_put_funny_stuff(u_char *command, u_char *args, u_char *subargs)
1016 {
1017 	u_char	*arg, *arg1;
1018 
1019 	if (my_strcmp(command, "NAMES") == 0)
1020 	{
1021 		arg1 = UP("-s");
1022 	}
1023 	else
1024 	if (my_strcmp(command, "LIST") == 0)
1025 	{
1026 		arg1 = UP("-g");
1027 	}
1028 	else
1029 	{
1030 		yell("--- icb_put_funny_stuff: not NAMES or LIST.");
1031 		return;
1032 	}
1033 	if (args && *args)
1034 	{
1035 		u_char	lbuf[255];
1036 
1037 		if ((arg = my_index(args, '*')) != NULL && (arg[1] == '\0' || isspace(arg[1])))
1038 			arg[0] = '.';
1039 		/* XXX */
1040 		if (my_strlen(args) > 251)
1041 			args[251] = 0;
1042 		snprintf(CP(lbuf), sizeof lbuf, "%s %s", arg1, args);
1043 		icb_put_who(lbuf);
1044 	}
1045 	else
1046 		icb_put_who(arg1);
1047 
1048 }
1049 
1050 void
icb_put_who(u_char * line)1051 icb_put_who(u_char *line)
1052 {
1053 	send_to_server("%cw%c%s", ICB_COMMAND, ICB_SEP, line);
1054 }
1055 
1056 static	void
icb_put_command(u_char * line)1057 icb_put_command(u_char *line)
1058 {
1059 	yell("--- icb_put_command not implemented yet");
1060 }
1061 
1062 void
icb_put_action(u_char * target,u_char * text)1063 icb_put_action(u_char *target, u_char *text)
1064 {
1065 	u_char	*s;
1066 	unsigned display;
1067 
1068 	s = new_malloc(2 + my_strlen(text) + 2 + 1);
1069 	strcpy(CP(s), "*");
1070 	strcat(CP(s), CP(text));
1071 	strcat(CP(s), "*");
1072 	display = set_display_off();
1073 	if (is_current_channel(target, get_from_server(), 0))
1074 		icb_put_public(s);
1075 	else
1076 		icb_put_msg2(target, s);
1077 	set_display(display);
1078 	new_free(&s);
1079 }
1080 
1081 /*
1082  * icb_split_msg(): split up `msg' into (upto) `ac' parts separate parts,
1083  * delimited by ICB_SEP, returning each part if to `ap', which points to
1084  * an array of u_char *'s `ac' long.  if ac is positive, and if there is
1085  * not enough room, this function returns -1, otherwise the number of
1086  * parts is returned.  if ac is negative, it is an indication that we
1087  * might have more fields, but we only want to split abs(ac);
1088  */
1089 static int
icb_split_msg(u_char * msg,u_char ** ap,int ac)1090 icb_split_msg(u_char *msg, u_char **ap, int ac)
1091 {
1092 	int	myac = 0, shortok = 0;
1093 	u_char	*s;
1094 
1095 	if (ac == 0)
1096 		return (-1);
1097 	else if (ac < 0)
1098 	{
1099 		shortok = 1;
1100 		ac = -ac;
1101 	}
1102 	ap[myac++] = msg;
1103 	while ((s = my_index(msg, ICB_SEP)))
1104 	{
1105 		if (myac >= ac)
1106 		{
1107 			if (shortok)
1108 				return (myac);
1109 			else
1110 				return (-1);
1111 		}
1112 		*s = '\0';
1113 		msg = s + 1;
1114 		ap[myac++] = msg;
1115 	}
1116 	return (myac);
1117 }
1118 
1119 /*
1120  * external hooks
1121  */
1122 
1123 /*
1124  * this function handles connecting to a server, and is called from
1125  * the guts of the server code.  we could send a login packet here
1126  * but we defer that until we get a protocol packet.
1127  */
1128 void
icb_login_to_server(int server)1129 icb_login_to_server(int server)
1130 {
1131 
1132 }
1133 
1134 /*
1135  * handle the icb protocol.
1136  */
1137 void
icb_parse_server(u_char * line)1138 icb_parse_server(u_char *line)
1139 {
1140 	int	len = (int)(u_char)*line++;
1141 	int	type = (int)*line++;
1142 	struct icb_server_status *status;
1143 
1144 	status = server_get_server_private(parsing_server());
1145 
1146 	(void)len;
1147 	icb_set_fromuserhost(NULL);
1148 	switch (type)
1149 	{
1150 	case ICB_LOGIN:
1151 		icb_got_login(line);
1152 		break;
1153 	case ICB_PUBLIC:
1154 		icb_got_public(line);
1155 		break;
1156 	case ICB_PERSONAL:
1157 		icb_got_msg(line);
1158 		break;
1159 	case ICB_STATUS:
1160 		icb_got_status(line);
1161 		break;
1162 	case ICB_ERROR:
1163 		icb_got_error(line);
1164 		break;
1165 	case ICB_IMPORTANT:
1166 		icb_got_important(line);
1167 		break;
1168 	case ICB_EXIT:
1169 		icb_got_exit(line);
1170 		break;
1171 	case ICB_CMDOUT:
1172 		icb_got_cmdout(line);
1173 		break;
1174 	case ICB_PROTO:
1175 		icb_got_proto(line);
1176 		break;
1177 	case ICB_BEEP:
1178 		icb_got_beep(line);
1179 		break;
1180 	case ICB_PING:
1181 		icb_got_ping(line);
1182 		break;
1183 	default:
1184 		icb_got_something(type, line);
1185 		break;
1186 	}
1187 	if (status && status->icb_got_group_waiting_for_members)
1188 		status->icb_got_group_waiting_for_members--;
1189 }
1190 
1191 /*
1192  * hook for user to send anything so the icb server; we only
1193  * provide one of these entry points for the user.  let the
1194  * rest be in scripts, or via some other IRC-like command
1195  * eg, send_text() will need to call into public/msg.
1196  *
1197  * format of this:
1198  *	type arg1 arg2 arg3 arg4 ...
1199  */
1200 void
icbcmd(u_char * command,u_char * args,u_char * subargs)1201 icbcmd(u_char *command, u_char *args, u_char *subargs)
1202 {
1203 	u_char	*type;
1204 	size_t	len;
1205 
1206 	if ((type = next_arg(args, &args)) != NULL)
1207 	{
1208 		len = my_strlen(type);
1209 		upper(type);
1210 
1211 		if (my_strncmp(type, "PUBLIC", len) == 0)
1212 			icb_put_public(args);
1213 		else
1214 		if (my_strncmp(type, "MSG", len) == 0)
1215 			icb_put_msg(args);
1216 		else
1217 		if (my_strncmp(type, "BEEP", len) == 0)
1218 			icb_put_beep(args);
1219 		else
1220 		if (my_strncmp(type, "BOOT", len) == 0)
1221 			icb_put_boot(args);
1222 		else
1223 		if (my_strncmp(type, "CANCEL", len) == 0)
1224 			icb_put_cancel(args);
1225 		else
1226 		if (my_strncmp(type, "ECHO", len) == 0)
1227 			icb_put_echo(args);
1228 		else
1229 		if (my_strncmp(type, "GROUP", len) == 0)
1230 			icb_put_group(args);
1231 		else
1232 		if (my_strncmp(type, "INVITE", len) == 0)
1233 			icb_put_invite(args);
1234 		else
1235 		if (my_strncmp(type, "MOTD", len) == 0)
1236 			icb_put_motd(args);
1237 		else
1238 		if (my_strncmp(type, "NICK", len) == 0)
1239 			icb_put_nick(args);
1240 		else
1241 		if (my_strncmp(type, "PASS", len) == 0)
1242 			icb_put_pass(args);
1243 		else
1244 		if (my_strncmp(type, "STATUS", len) == 0)
1245 			icb_put_status(args);
1246 		else
1247 		if (my_strncmp(type, "TOPIC", len) == 0)
1248 			icb_put_topic(args);
1249 		else
1250 		if (my_strncmp(type, "VERSION", len) == 0)
1251 			icb_put_version(args);
1252 		else
1253 		if (my_strncmp(type, "WHO", len) == 0)
1254 			icb_put_who(args);
1255 		else
1256 		if (my_strncmp(type, "COMMAND", len) == 0)
1257 			icb_put_command(args);
1258 		else
1259 			say("No such ICB command %s", type);
1260 	}
1261 }
1262 
1263 int
icb_port(void)1264 icb_port(void)
1265 {
1266 	return icb_port_local;
1267 }
1268 
1269 void
set_icb_port(int port)1270 set_icb_port(int port)
1271 {
1272 	icb_port_local = port;
1273 }
1274