1 /*
2  * parse.c: handles messages from the server.   Believe it or not.  I
3  * certainly wouldn't if I were you.
4  *
5  * Written By Michael Sandrof
6  *
7  * Copyright (c) 1990 Michael Sandrof.
8  * Copyright (c) 1991, 1992 Troy Rollo.
9  * Copyright (c) 1992-2021 Matthew R. Green.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "irc.h"
37 IRCII_RCSID("@(#)$eterna: parse.c,v 1.123 2021/02/27 08:00:42 mrg Exp $");
38 
39 #include "server.h"
40 #include "names.h"
41 #include "vars.h"
42 #include "ctcp.h"
43 #include "hook.h"
44 #include "edit.h"
45 #include "ignore.h"
46 #include "whois.h"
47 #include "lastlog.h"
48 #include "ircaux.h"
49 #include "funny.h"
50 #include "irccrypt.h"
51 #include "ircterm.h"
52 #include "flood.h"
53 #include "window.h"
54 #include "screen.h"
55 #include "output.h"
56 #include "numbers.h"
57 #include "parse.h"
58 #include "notify.h"
59 #include "notice.h"
60 #include "alias.h"
61 
62 #define STRING_CHANNEL '+'
63 #define MULTI_CHANNEL '#'
64 #define LOCAL_CHANNEL '&'
65 #define SAFE_CHANNEL '!'
66 
67 #define	MAXPARA	15	/* Taken from the ircd */
68 
69 static	void	break_args(u_char *, u_char **, u_char **);
70 static	void	p_linreply(u_char *, u_char *, u_char **);
71 static	void	p_ping(u_char *, u_char *, u_char **);
72 static	void	p_topic(u_char *, u_char *, u_char **);
73 static	void	p_wall(u_char *, u_char *, u_char **);
74 static	void	p_wallops(u_char *, u_char **);
75 static	void	p_privmsg(u_char *, u_char *, u_char **);
76 static	void	p_quit(u_char *, u_char **);
77 static	void	p_pong(u_char *, u_char *, u_char **);
78 static	void	p_error(u_char *, u_char **);
79 static	void	p_channel(u_char *, u_char *, u_char **);
80 static	void	p_invite(u_char *, u_char **);
81 static	void	p_server_kill(u_char *, u_char **);
82 static	void	p_nick(u_char *, u_char *, u_char **);
83 static	void	p_mode(u_char *, u_char *, u_char **);
84 static	void	p_kick(u_char *, u_char *, u_char **);
85 static	void	p_part(u_char *, u_char *, u_char **);
86 
87 /* User and host information from server 2.7 */
88 static	u_char	*FromUserHost = NULL;
89 
90 /* doing a PRIVMSG */
91 static	int	doing_privmsg_active = 0;
92 
93 /*
94  * joined_nick: the nickname of the last person who joined the current
95  * channel
96  */
97 static	u_char	*joined_nick = NULL;
98 
99 /* public_nick: nick of the last person to send a message to your channel */
100 static	u_char	*public_nick = NULL;
101 
102 /*
103  * is_channel: determines if the argument is a channel.  If it's a number,
104  * begins with MULTI_CHANNEL and has no '*', or STRING_CHANNEL, then its a
105  * channel.
106  */
107 int
is_channel(u_char * to)108 is_channel(u_char *to)
109 {
110 	int	version;
111 	const	int	from_server = get_from_server();
112 
113 	if (to == 0)
114 		return (0);
115 
116 	version = server_get_version(from_server);
117 	if (version == ServerICB)
118 		return (my_stricmp(to, server_get_icbgroup(from_server)) == 0);
119 	else
120 		return ((version < Server2_7 && (isdigit(*to) || (*to == STRING_CHANNEL) || *to == '-'))
121 		     || (version > Server2_6 && *to == MULTI_CHANNEL)
122 		     || (version > Server2_7 && *to == LOCAL_CHANNEL)
123 		     || (version > Server2_8 && *to == STRING_CHANNEL)
124 		     || (version > Server2_9 && *to == SAFE_CHANNEL));
125 }
126 
127 
128 /*
129  * paste_args: re-connect a string broken up with break_args(), with a
130  * starting point.  paste_args(ArgList, 2) will end up with 3 strings
131  * in ArgList[0], ArgList[1] the same as post-break_args(), and with
132  * all the remaining arguments in ArgList[2].
133  */
134 u_char	*
paste_args(u_char ** Args,int StartPoint)135 paste_args(u_char **Args, int StartPoint)
136 {
137 	int	i;
138 
139 	for (; StartPoint; Args++, StartPoint--)
140 		if (!*Args)
141 			return NULL;
142 	for (i = 0; Args[i] && Args[i+1]; i++)
143 		Args[i][my_strlen(Args[i])] = ' ';
144 	Args[1] = NULL;
145 	return Args[0];
146 }
147 
148 /*
149  * break_args: breaks up the line from the server, in to where its from,
150  * setting FromUserHost if it should be, and returns all the arguements
151  * that are there.   Re-written by phone, dec 1992.
152  *
153  * using this function will leave you will a NULL terminated list, and
154  * any attempt to use any argument must ensure that all the previous
155  * entries are not NULL.  eg, do not check or use for ArgList[1] without
156  * first making sure ArgList[0] is not NULL.
157  */
158 static	void
break_args(u_char * Input,u_char ** Sender,u_char ** OutPut)159 break_args(u_char *Input, u_char **Sender, u_char **OutPut)
160 {
161 	u_char	*s = Input,
162 		*t;
163 	int	ArgCount = 0;
164 
165 	/*
166 	 * Get sender from :sender and user@host if :nick!user@host
167 	 */
168 	FromUserHost = NULL;
169 
170 	if (*Input == ':')
171 	{
172 		u_char	*tmp;
173 
174 		*Input++ = '\0';
175 		if ((s = (u_char *) my_index(Input, ' ')) != NULL)
176 			*s++ = '\0';
177 		*Sender = Input;
178 		if ((tmp = (u_char *) my_index(*Sender, '!')) != NULL)
179 		{
180 			*tmp++ = '\0';
181 			FromUserHost = tmp;
182 		}
183 	}
184 	else
185 		*Sender = empty_string();
186 
187 	if (s)
188 	{
189 		for (;;)
190 		{
191 			while (*s == ' ')
192 				*s++ = '\0';
193 
194 			if (!*s)
195 				break;
196 
197 			if (*s == ':')
198 			{
199 				for (t = s; *t; t++)
200 					*t = *(t + 1);
201 				OutPut[ArgCount++] = s;
202 				break;
203 			}
204 			OutPut[ArgCount++] = s;
205 			if (ArgCount >= MAXPARA)
206 				break;
207 
208 			for (; *s != ' ' && *s; s++)
209 				;
210 		}
211 	}
212 	while (ArgCount <= MAXPARA)
213 		OutPut[ArgCount++] = NULL;
214 }
215 
216 /* beep_em: Not hard to figure this one out */
217 void
beep_em(int beeps)218 beep_em(int beeps)
219 {
220 	int	cnt,
221 		i;
222 
223 	for (cnt = beeps, i = 0; i < cnt; i++)
224 		term_beep();
225 }
226 
227 /* in response to a TOPIC message from the server */
228 static	void
p_topic(u_char * comm,u_char * from,u_char ** ArgList)229 p_topic(u_char *comm, u_char *from, u_char **ArgList)
230 {
231 	int	flag;
232 
233 	if (check_params(comm, from, 1, ArgList, 1))
234 		return;
235 
236 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
237 	if (flag == IGNORED)
238 		return;
239 
240 	save_message_from();
241 	if (!ArgList[1])
242 	{
243 		message_from(NULL, LOG_CRAP);
244 		if (do_hook(TOPIC_LIST, "%s * %s", from, ArgList[0]))
245 			say("%s has changed the topic to %s", from, ArgList[0]);
246 	}
247 	else
248 	{
249 		message_from(ArgList[0], LOG_CRAP);
250 		if (do_hook(TOPIC_LIST, "%s %s %s", from, ArgList[0], ArgList[1]))
251 			say("%s has changed the topic on channel %s to %s",
252 				from, ArgList[0], ArgList[1]);
253 	}
254 	restore_message_from();
255 }
256 
257 static	void
p_linreply(u_char * comm,u_char * from,u_char ** ArgList)258 p_linreply(u_char *comm, u_char *from, u_char **ArgList)
259 {
260 	if (check_params(comm, from, 0, ArgList, 1))
261 		return;
262 
263 	paste_args(ArgList, 0);
264 	say("%s", ArgList[0]);
265 }
266 
267 static	void
p_wall(u_char * comm,u_char * from,u_char ** ArgList)268 p_wall(u_char *comm, u_char *from, u_char **ArgList)
269 {
270 	int	flag,
271 		level;
272 	u_char	*line;
273 	u_char	*high;
274 
275 	if (check_params(comm, from, 0, ArgList, 1))
276 		return;
277 
278 	paste_args(ArgList, 0);
279 	line = ArgList[0];
280 	flag = double_ignore(from, from_user_host(), IGNORE_WALLS);
281 	save_message_from();
282 	message_from(from, LOG_WALL);
283 	if (flag != IGNORED)
284 	{
285 		if (flag == HIGHLIGHTED)
286 			high = highlight_str();
287 		else
288 			high = empty_string();
289 		if ((flag != DONT_IGNORE) && (ignore_usernames() & IGNORE_WALLS)
290 				&& !from_user_host())
291 			add_to_whois_queue(from, whois_ignore_walls, "%s",line);
292 		else
293 		{
294 			level = set_lastlog_msg_level(LOG_WALL);
295 			if (check_flooding(from,
296 			    server_get_nickname(parsing_server()),
297 			    WALL_FLOOD, line) && do_hook(WALL_LIST, "%s %s",
298 						     from, line))
299 				put_it("%s#%s#%s %s", high, from, high, line);
300 			if (do_beep_on_level(LOG_WALL))
301 				beep_em(1);
302 			set_lastlog_msg_level(level);
303 		}
304 	}
305 	restore_message_from();
306 }
307 
308 static	void
p_wallops(u_char * from,u_char ** ArgList)309 p_wallops(u_char *from, u_char **ArgList)
310 {
311 	int	flag, level;
312 	u_char	*line;
313 
314 	if (!from)
315 		return;
316 	if (!(line = paste_args(ArgList, 0)))
317 		return;
318 	flag = double_ignore(from, from_user_host(), IGNORE_WALLOPS);
319 	level = set_lastlog_msg_level(LOG_WALLOP);
320 	save_message_from();
321 	message_from(from, LOG_WALLOP);
322 	if (my_index(from, '.'))
323 	{
324 		u_char	*high;
325 
326 		if (flag != IGNORED)
327 		{
328 			if (flag == HIGHLIGHTED)
329 				high = highlight_str();
330 			else
331 				high = empty_string();
332 			if (do_hook(WALLOP_LIST, "%s S %s", from, line))
333 				put_it("%s!%s!%s %s", high, from, high, line);
334 			if (do_beep_on_level(LOG_WALLOP))
335 				beep_em(1);
336 		}
337 	}
338 	else
339 	{
340 		if (get_int_var(USER_WALLOPS_VAR))
341 		{
342 			if (flag != DONT_IGNORE &&
343 			    check_flooding(from,
344 					   server_get_nickname(parsing_server()),
345 					   WALLOP_FLOOD, line))
346 				add_to_whois_queue(from, whois_new_wallops, "%s", line);
347 		}
348 		else if (my_strcmp(from, server_get_nickname(get_window_server(0))) != 0)
349 			put_it("!%s! %s", from, line);
350 	}
351 	set_lastlog_msg_level(level);
352 	restore_message_from();
353 }
354 
355 void
whoreply(u_char * from,u_char ** ArgList)356 whoreply(u_char *from, u_char **ArgList)
357 {
358 	static	u_char	format[40];
359 	static	int	last_width = -1;
360 	int	ok;
361 	u_char	*channel,
362 		*user,
363 		*host,
364 		*server,
365 		*nick,
366 		*status,
367 		*name;
368 	int	i;
369 
370 	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
371 	{
372 		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
373 		    snprintf(CP(format), sizeof format, "%%-%u.%us %%-9s %%-3s %%s@%%s (%%s)",
374 					(u_char) last_width,
375 					(u_char) last_width);
376 		else
377 		    my_strcpy(format, UP("%s\t%-9s %-3s %s@%s (%s)"));
378 	}
379 	i = 0;
380 	channel = user = host = server = nick = status = name = empty_string();
381 	if (ArgList[i])
382 		channel = ArgList[i++];
383 	if (ArgList[i])
384 		user = ArgList[i++];
385 	if (ArgList[i])
386 		host = ArgList[i++];
387 	if (ArgList[i])
388 		server = ArgList[i++];
389 	if (ArgList[i])
390 		nick = ArgList[i++];
391 	if (ArgList[i])
392 		status = ArgList[i++];
393 	paste_args(ArgList, i);
394 
395 	if (ArgList[i])
396 		name = ArgList[i];
397 
398 	ok = whoreply_check(channel, user, host, server, nick, status, name, format);
399 	if (ok)
400 	{
401 		if (do_hook(WHO_LIST, "%s %s %s %s %s %s", channel, nick,
402 				status, user, host, name))
403 		{
404 			if (get_int_var(SHOW_WHO_HOPCOUNT_VAR))
405 				put_it(CP(format), channel, nick, status, user, host,
406 					name);
407 			else
408 			{
409 				u_char	*tmp;
410 
411 				if ((tmp = (u_char *) my_index(name, ' ')) !=
412 								NULL)
413 					tmp++;
414 				else
415 					tmp = name;
416 				put_it(CP(format), channel, nick, status, user, host,
417 					tmp);
418 			}
419 		}
420 	}
421 }
422 
423 static	void
p_privmsg(u_char * comm,u_char * from,u_char ** ArgList)424 p_privmsg(u_char *comm, u_char *from, u_char **ArgList)
425 {
426 	int	level,
427 		flag,
428 		list_type,
429 		flood_type,
430 		log_type;
431 	u_char	ignore_type;
432 	u_char	*ptr,
433 		*to;
434 	u_char	*high;
435 	int	no_flood;
436 
437 	if (check_params(comm, from, 0, ArgList, 2))
438 		return;
439 
440 	paste_args(ArgList, 1);
441 	to = ArgList[0];
442 	ptr = ArgList[1];
443 	save_message_from();
444 	if (is_channel(to))
445 	{
446 		malloc_strcpy(&public_nick, from);
447 		if (!is_on_channel(to, parsing_server(), from))
448 		{
449 			log_type = LOG_PUBLIC;
450 			ignore_type = IGNORE_PUBLIC;
451 			list_type = PUBLIC_MSG_LIST;
452 			flood_type = PUBLIC_FLOOD;
453 		}
454 		else
455 		{
456 			log_type = LOG_PUBLIC;
457 			ignore_type = IGNORE_PUBLIC;
458 			if (is_current_channel(to, parsing_server(), 0))
459 				list_type = PUBLIC_LIST;
460 			else
461 				list_type = PUBLIC_OTHER_LIST;
462 			flood_type = PUBLIC_FLOOD;
463 		}
464 		message_from(to, log_type);
465 	}
466 	else
467 	{
468 		flood_type = MSG_FLOOD;
469 		if (my_stricmp(to, server_get_nickname(parsing_server())))
470 		{
471 			log_type = LOG_WALL;
472 			ignore_type = IGNORE_WALLS;
473 			list_type = MSG_GROUP_LIST;
474 		}
475 		else
476 		{
477 			log_type = LOG_MSG;
478 			ignore_type = IGNORE_MSGS;
479 			list_type = MSG_LIST;
480 		}
481 		message_from(from, log_type);
482 	}
483 	flag = double_ignore(from, from_user_host(), ignore_type);
484 	switch (flag)
485 	{
486 	case IGNORED:
487 		if ((list_type == MSG_LIST) && get_int_var(SEND_IGNORE_MSG_VAR))
488 			send_to_server("NOTICE %s :%s is ignoring you", from,
489 					server_get_nickname(parsing_server()));
490 		goto out;
491 	case HIGHLIGHTED:
492 		high = highlight_str();
493 		break;
494 	default:
495 		high = empty_string();
496 		break;
497 	}
498 	level = set_lastlog_msg_level(log_type);
499 	ptr = do_ctcp(from, to, ptr);
500 	if (!ptr || !*ptr)
501 		goto out;
502 	if ((flag != DONT_IGNORE) && (ignore_usernames() & ignore_type) && !from_user_host())
503 		add_to_whois_queue(from, whois_ignore_msgs, "%s", ptr);
504 	else
505 	{
506 		no_flood = check_flooding(from, to, flood_type, ptr);
507 		if (ctcp_was_crypted() == 0 || do_hook(ENCRYPTED_PRIVMSG_LIST,"%s %s %s",from, to, ptr))
508 		{
509 		switch (list_type)
510 		{
511 		case PUBLIC_MSG_LIST:
512 			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
513 			    put_it("%s(%s/%s)%s %s", high, from, to, high, ptr);
514 			break;
515 		case MSG_GROUP_LIST:
516 			if (no_flood && do_hook(list_type, "%s %s %s", from, to, ptr))
517 			    put_it("%s-%s:%s-%s %s", high, from, to, high, ptr);
518 			break;
519 		case MSG_LIST:
520 			if (!no_flood)
521 				break;
522 			set_recv_nick(from);
523 			if (is_away_set())
524 				beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));
525 			if (do_hook(list_type, "%s %s", from, ptr))
526 			{
527 			    if (is_away_set())
528 			    {
529 				time_t t;
530 				u_char *msg = NULL;
531 				size_t len = my_strlen(ptr) + 20;
532 
533 				t = time(NULL);
534 				msg = new_malloc(len);
535 				snprintf(CP(msg), len, "%s <%.16s>", ptr, ctime(&t));
536 				put_it("%s*%s*%s %s", high, from, high, msg);
537 				new_free(&msg);
538 			    }
539 			    else
540 				put_it("%s*%s*%s %s", high, from, high, ptr);
541 			}
542 			break;
543 		case PUBLIC_LIST:
544 			if (get_int_var(MAKE_NOTICE_MSG_VAR))
545 				doing_privmsg_active = 1;
546 			if (no_flood && do_hook(list_type, "%s %s %s", from,
547 			    to, ptr))
548 				put_it("%s<%s>%s %s", high, from, high, ptr);
549 			doing_privmsg_active = 0;
550 			break;
551 		case PUBLIC_OTHER_LIST:
552 			if (get_int_var(MAKE_NOTICE_MSG_VAR))
553 				doing_privmsg_active = 1;
554 			if (no_flood && do_hook(list_type, "%s %s %s", from,
555 			    to, ptr))
556 				put_it("%s<%s:%s>%s %s", high, from, to, high,
557 					ptr);
558 			doing_privmsg_active = 0;
559 			break;
560 		}
561 		if (do_beep_on_level(log_type))
562 			beep_em(1);
563 		}
564 	}
565 	set_lastlog_msg_level(level);
566 out:
567 	restore_message_from();
568 }
569 
570 static	void
p_quit(u_char * from,u_char ** ArgList)571 p_quit(u_char *from, u_char **ArgList)
572 {
573 	int	one_prints = 0;
574 	u_char	*chan;
575 	u_char	*Reason;
576 	int	flag;
577 
578 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
579 	save_message_from();
580 	if (flag != IGNORED)
581 	{
582 		paste_args(ArgList, 0);
583 		Reason = ArgList[0] ? ArgList[0] : (u_char *) "?";
584 		for (chan = walk_channels(from, 1, parsing_server()); chan; chan = walk_channels(from, 0, -1))
585 		{
586 			message_from(chan, LOG_CRAP);
587 			if (do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", chan, from, Reason))
588 				one_prints = 1;
589 		}
590 		if (one_prints)
591 		{
592 			message_from(what_channel(from, parsing_server()), LOG_CRAP);
593 			if (do_hook(SIGNOFF_LIST, "%s %s", from, Reason))
594 				say("Signoff: %s (%s)", from, Reason);
595 		}
596 	}
597 	message_from(NULL, LOG_CURRENT);
598 	remove_from_channel(NULL, from, parsing_server());
599 	notify_mark(from, 0, 0);
600 	restore_message_from();
601 }
602 
603 static	void
p_pong(u_char * comm,u_char * from,u_char ** ArgList)604 p_pong(u_char *comm, u_char *from, u_char **ArgList)
605 {
606 	int	flag;
607 
608 	if (check_params(comm, from, 0, ArgList, 1))
609 		return;
610 
611 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
612 	if (flag == IGNORED)
613 		return;
614 
615 	if (ArgList[0])
616 		say("%s: PONG received from %s", ArgList[0], from);
617 }
618 
619 static	void
p_error(u_char * from,u_char ** ArgList)620 p_error(u_char *from, u_char **ArgList)
621 {
622 	if (ArgList[0] == NULL)
623 		return;
624 	paste_args(ArgList, 0);
625 	say("%s", ArgList[0]);
626 }
627 
628 static	void
p_channel(u_char * comm,u_char * from,u_char ** ArgList)629 p_channel(u_char *comm, u_char *from, u_char **ArgList)
630 {
631 	int	join;
632 	u_char	*channel;
633 	int	flag;
634 	u_char	*s, *ov = NULL;
635 	int	chan_oper = 0, chan_voice = 0;
636 
637 	if (check_params(comm, from, 0, ArgList, 1))
638 		return;
639 
640 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
641 	if (my_strcmp(ArgList[0], zero()))
642 	{
643 		join = 1;
644 		channel = ArgList[0];
645 		/*
646 		 * this \007 should be \a but a lot of compilers are
647 		 * broken.  *sigh*  -mrg
648 		 */
649 		if ((ov = s = my_index(channel, '\007')))
650 		{
651 			*s = '\0';
652 			ov++;
653 			while (*++s)
654 			{
655 				if (*s == 'o')
656 					chan_oper = 1;
657 				if (*s == 'v')
658 					chan_voice = 1;
659 
660 			}
661 		}
662 		malloc_strcpy(&joined_nick, from);
663 	}
664 	else
665 	{
666 		channel = zero();
667 		join = 0;
668 	}
669 	if (!my_stricmp(from, server_get_nickname(parsing_server())))
670 	{
671 		if (join)
672 		{
673 			add_channel(channel, 0, parsing_server(), CHAN_JOINED, NULL);
674 			send_to_server("MODE %s", channel);
675 		}
676 		else
677 			remove_channel(channel, parsing_server());
678 	}
679 	else
680 	{
681 		save_message_from();
682 		message_from(channel, LOG_CRAP);
683 		if (join)
684 			add_to_channel(channel, from, parsing_server(), chan_oper, chan_voice);
685 		else
686 			remove_from_channel(channel, from, parsing_server());
687 		restore_message_from();
688 	}
689 	if (join)
690 	{
691 		if (!get_channel_oper(channel, parsing_server()))
692 			set_in_on_who(1);
693 		save_message_from();
694 		message_from(channel, LOG_CRAP);
695 		if (flag != IGNORED && do_hook(JOIN_LIST, "%s %s %s", from,
696 						channel, ov ? ov : (u_char *) ""))
697 		{
698 			if (from_user_host())
699 				if (ov && *ov)
700 					say("%s (%s) has joined channel %s +%s", from,
701 				    from_user_host(), channel, ov);
702 				else
703 					say("%s (%s) has joined channel %s", from,
704 				    from_user_host(), channel);
705 			else
706 				if (ov && *ov)
707 					say("%s has joined channel %s +%s", from,
708 					channel, ov);
709 				else
710 					say("%s has joined channel %s", from, channel);
711 		}
712 		restore_message_from();
713 		set_in_on_who(0);
714 	}
715 }
716 
717 static	void
p_invite(u_char * from,u_char ** ArgList)718 p_invite(u_char *from, u_char **ArgList)
719 {
720 	u_char	*high;
721 	int	flag;
722 
723 	if (!from)
724 		return;
725 	flag = double_ignore(from, from_user_host(), IGNORE_INVITES);
726 	switch (flag)
727 	{
728 	case IGNORED:
729 		if (get_int_var(SEND_IGNORE_MSG_VAR))
730 			send_to_server("NOTICE %s :%s is ignoring you",
731 				from, server_get_nickname(parsing_server()));
732 		return;
733 	case HIGHLIGHTED:
734 		high = highlight_str();
735 		break;
736 	default:
737 		high = empty_string();
738 		break;
739 	}
740 	if (ArgList[0] && ArgList[1])
741 	{
742 		if ((flag != DONT_IGNORE) && (ignore_usernames() & IGNORE_INVITES)
743 		    && !from_user_host())
744 			add_to_whois_queue(from, whois_ignore_invites,
745 					"%s", ArgList[1]);
746 		else
747 		{
748 			save_message_from();
749 			message_from(from, LOG_CRAP);
750 			if (do_hook(INVITE_LIST, "%s %s", from, ArgList[1]))
751 				say("%s%s%s invites you to channel %s", high,
752 						from, high, ArgList[1]);
753 			restore_message_from();
754 			set_invite_channel(ArgList[1]);
755 			set_recv_nick(from);
756 		}
757 	}
758 }
759 
760 static	void
p_server_kill(u_char * from,u_char ** ArgList)761 p_server_kill(u_char *from, u_char **ArgList)
762 {
763 	/*
764 	 * this is so bogus checking for a server name having a '.'
765 	 * in it - phone, april 1993.
766 	 */
767 	if (my_index(from, '.'))
768 		say("You have been rejected by server %s", from);
769 	else
770 		say("You have been killed by operator %s %s", from,
771 			ArgList[0] && ArgList[1] ?
772 			ArgList[1] : (u_char *) "(No Reason Given)");
773 	close_server(parsing_server(), empty_string());
774 	window_check_servers();
775 	if (!connected_to_server())
776 		say("Use /SERVER to reconnect to a server");
777 }
778 
779 static	void
p_ping(u_char * comm,u_char * from,u_char ** ArgList)780 p_ping(u_char *comm, u_char *from, u_char **ArgList)
781 {
782 	if (check_params(comm, from, 0, ArgList, 1))
783 		return;
784 
785 	send_to_server("PONG :%s", paste_args(ArgList, 0));
786 }
787 
788 static	void
p_nick(u_char * comm,u_char * from,u_char ** ArgList)789 p_nick(u_char *comm, u_char *from, u_char **ArgList)
790 {
791 	int	one_prints = 0,
792 		its_me = 0;
793 	u_char	*chan;
794 	u_char	*line;
795 	int	flag;
796 
797 	if (check_params(comm, from, 0, ArgList, 1))
798 		return;
799 
800 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
801 	line = ArgList[0];
802 	if (my_stricmp(from, server_get_nickname(parsing_server())) == 0){
803 		if (parsing_server() == get_primary_server())
804 			set_nickname(line);
805 		server_set_nickname(parsing_server(), line);
806 		its_me = 1;
807 	}
808 	save_message_from();
809 	if (flag != IGNORED)
810 	{
811 		for (chan = walk_channels(from, 1, parsing_server()); chan;
812 				chan = walk_channels(from, 0, -1))
813 		{
814 			message_from(chan, LOG_CRAP);
815 			if (do_hook(CHANNEL_NICK_LIST, "%s %s %s", chan, from, line))
816 				one_prints = 1;
817 		}
818 		if (one_prints)
819 		{
820 			if (its_me)
821 				message_from(NULL, LOG_CRAP);
822 			else
823 				message_from(what_channel(from, parsing_server()), LOG_CRAP);
824 			if (do_hook(NICKNAME_LIST, "%s %s", from, line))
825 				say("%s is now known as %s", from, line);
826 		}
827 	}
828 	rename_nick(from, line, parsing_server());
829 	if (my_stricmp(from, line))
830 	{
831 		message_from(NULL, LOG_CURRENT);
832 		notify_mark(from, 0, 0);
833 		notify_mark(line, 1, 0);
834 	}
835 	restore_message_from();
836 }
837 
838 static	void
p_mode(u_char * comm,u_char * from,u_char ** ArgList)839 p_mode(u_char *comm, u_char *from, u_char **ArgList)
840 {
841 	u_char	*channel;
842 	u_char	*line;
843 	int	flag;
844 
845 	if (check_params(comm, from, 0, ArgList, 2))
846 		return;
847 
848 	paste_args(ArgList, 1);
849 	channel = ArgList[0];
850 	line = ArgList[1];
851 
852 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
853 	save_message_from();
854 	message_from(channel, LOG_CRAP);
855 	if (is_channel(channel))
856 	{
857 		if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s",
858 				from, channel, line))
859 			say("Mode change \"%s\" on channel %s by %s",
860 					line, channel, from);
861 		update_channel_mode(channel, parsing_server(), line);
862 	}
863 	else
864 	{
865 		if (flag != IGNORED && do_hook(MODE_LIST, "%s %s %s",
866 				from, channel, line))
867 			say("Mode change \"%s\" for user %s by %s",
868 					line, channel, from);
869 		update_user_mode(line);
870 	}
871 	update_all_status();
872 	restore_message_from();
873 }
874 
875 static	void
p_kick(u_char * comm,u_char * from,u_char ** ArgList)876 p_kick(u_char *comm, u_char *from, u_char **ArgList)
877 {
878 	u_char	*channel,
879 		*who,
880 		*comment;
881 
882 	if (check_params(comm, from, 0, ArgList, 2))
883 		return;
884 
885 	channel = ArgList[0];
886 	who = ArgList[1];
887 	comment = ArgList[2];
888 
889 	save_message_from();
890 	message_from(channel, LOG_CRAP);
891 	if (my_stricmp(who, server_get_nickname(parsing_server())) == 0)
892 	{
893 		if (comment && *comment)
894 		{
895 			if (do_hook(KICK_LIST, "%s %s %s %s", who,
896 					from, channel, comment))
897 				say("You have been kicked off channel %s by %s (%s)",
898 					channel, from, comment);
899 		}
900 		else
901 		{
902 			if (do_hook(KICK_LIST, "%s %s %s", who, from,
903 					channel))
904 				say("You have been kicked off channel %s by %s",
905 					channel, from);
906 		}
907 		remove_channel(channel, parsing_server());
908 		update_all_status();
909 	}
910 	else
911 	{
912 		if (comment && *comment)
913 		{
914 			if (do_hook(KICK_LIST, "%s %s %s %s", who,
915 					from, channel, comment))
916 				say("%s has been kicked off channel %s by %s (%s)",
917 					who, channel, from, comment);
918 		}
919 		else
920 		{
921 			if (do_hook(KICK_LIST, "%s %s %s", who, from,
922 					channel))
923 				say("%s has been kicked off channel %s by %s",
924 					who, channel, from);
925 		}
926 		remove_from_channel(channel, who, parsing_server());
927 	}
928 	restore_message_from();
929 }
930 
931 static	void
p_part(u_char * comm,u_char * from,u_char ** ArgList)932 p_part(u_char *comm, u_char *from, u_char **ArgList)
933 {
934 	u_char	*channel;
935 	u_char	*comment;
936 	int	flag;
937 
938 	if (check_params(comm, from, 0, ArgList, 1))
939 		return;
940 
941 	channel = ArgList[0];
942 	flag = double_ignore(from, from_user_host(), IGNORE_CRAP);
943 	if (!is_on_channel(channel, parsing_server(), from))
944 		return;
945 	comment = ArgList[1];
946 	if (!comment)
947 		comment = empty_string();
948 	set_in_on_who(1);
949 	if (flag != IGNORED)
950 	{
951 		save_message_from();
952 		message_from(channel, LOG_CRAP);
953 		if (do_hook(LEAVE_LIST, "%s %s %s", from, channel, comment))
954 		{
955 			if (comment && *comment != '\0')
956 				say("%s has left channel %s (%s)", from, channel, comment);
957 			else
958 				say("%s has left channel %s", from, channel);
959 		}
960 		restore_message_from();
961 	}
962 	if (my_stricmp(from, server_get_nickname(parsing_server())) == 0)
963 		remove_channel(channel, parsing_server());
964 	else
965 		remove_from_channel(channel, from, parsing_server());
966 	set_in_on_who(0);
967 }
968 
969 /*
970  * irc2_odd_server_stuff(): call this if you parse an IRC message and
971  * it doesn't make sense and perhaps should be warned about.
972  */
973 void
irc2_odd_server_stuff(u_char * comm,u_char * from,u_char ** ArgList)974 irc2_odd_server_stuff(u_char *comm, u_char *from, u_char **ArgList)
975 {
976 	u_char *args;
977 
978 	paste_args(ArgList, 0);
979 	args = ArgList[0];
980 	if (!args)
981 		args = empty_string();
982 
983 	if (from)
984 		say("Odd server stuff: \"%s %s\" (%s)", comm, args, from);
985 	else
986 		say("Odd server stuff: \"%s %s\"", comm, args);
987 }
988 
989 /*
990  * front end to irc2_odd_server_stuff() with argument checking.
991  * 'comm', 'from', and 'args' are passed to irc2_odd_server_stuff()
992  * if check_from is set and from is NULL, or, if any array index
993  * smaller than arg_count is NULL (ie, array must have arg_count
994  * or more entries.)
995  */
996 int
check_params(u_char * comm,u_char * from,int check_from,u_char ** args,unsigned arg_count)997 check_params(u_char *comm, u_char *from, int check_from,
998 	     u_char **args, unsigned arg_count)
999 {
1000 	int fail = 0;
1001 
1002 	if (check_from && from == NULL)
1003 		fail = 1;
1004 	else
1005 	{
1006 		int i;
1007 
1008 		for (i = 0; i < arg_count; i++)
1009 			if (args[i] == NULL)
1010 			{
1011 				fail = 1;
1012 				break;
1013 			}
1014 	}
1015 
1016 	if (fail)
1017 	{
1018 		irc2_odd_server_stuff(comm, from, args);
1019 		return 1;
1020 	}
1021 
1022 	return 0;
1023 }
1024 
1025 void
irc2_parse_server(u_char * line)1026 irc2_parse_server(u_char *line)
1027 {
1028 	u_char	*from,
1029 		*comm,
1030 		*end,
1031 		*copy = NULL;
1032 	int	numeric;
1033 	u_char	**ArgList;
1034 	u_char	*TrueArgs[MAXPARA + 1];
1035 
1036 	if (NULL == line)
1037 		return;
1038 
1039 	end = my_strlen(line) + line - 1;
1040 	if (*end == '\n') {
1041 		*end = '\0';
1042 		if (end > line)
1043 			end--;
1044 	}
1045 	if (*end == '\r')
1046 		*end = '\0';
1047 
1048 	if (end < line)
1049 	{
1050 		yell("--- HEY BAD BOO");
1051 	}
1052 
1053 	if (*line == ':')
1054 	{
1055 		if (!do_hook(RAW_IRC_LIST, "%s", line + 1))
1056 			return;
1057 	}
1058 	else if (!do_hook(RAW_IRC_LIST, "%s %s", "*", line))
1059 		return;
1060 
1061 	malloc_strcpy(&copy, line);
1062 	ArgList = TrueArgs;
1063 	break_args(copy, &from, ArgList);
1064 
1065 	if (!(comm = (*ArgList++)))
1066 		goto out;		/* Empty line from server - ByeBye */
1067 
1068 	/*
1069 	 * At this point, from and comm are both valid pointers, but the
1070 	 * ArgList may start with a NULL.  Not all the functions called
1071 	 * from here require that the ArgList[0] be a valid string, so each
1072 	 * must check any consumed ArgList[] component is valid.
1073 	 */
1074 
1075 	/*
1076 	 * only allow numbers 1 .. 999.
1077 	 */
1078 	if ((numeric = my_atoi(comm)) > 0 && numeric < 1000)
1079 		numbered_command(comm, from, numeric, ArgList);
1080 	else if (my_strcmp(comm, "NAMREPLY") == 0)
1081 		funny_namreply(comm, from, ArgList);
1082 	else if (my_strcmp(comm, "WHOREPLY") == 0)
1083 		whoreply(from, ArgList);
1084 	else if (my_strcmp(comm, "NOTICE") == 0)
1085 		parse_notice(comm, from, ArgList);
1086 	/* everything else is handled locally */
1087 	else if (my_strcmp(comm, "PRIVMSG") == 0)
1088 		p_privmsg(comm, from, ArgList);
1089 	else if (my_strcmp(comm, "JOIN") == 0)
1090 		p_channel(comm, from, ArgList);
1091 	else if (my_strcmp(comm, "PART") == 0)
1092 		p_part(comm, from, ArgList);
1093 	else if (my_strcmp(comm, "CHANNEL") == 0)
1094 		p_channel(comm, from, ArgList);
1095 	else if (my_strcmp(comm, "QUIT") == 0)
1096 		p_quit(from, ArgList);
1097 	else if (my_strcmp(comm, "WALL") == 0)
1098 		p_wall(comm, from, ArgList);
1099 	else if (my_strcmp(comm, "WALLOPS") == 0)
1100 		p_wallops(from, ArgList);
1101 	else if (my_strcmp(comm, "LINREPLY") == 0)
1102 		p_linreply(comm, from, ArgList);
1103 	else if (my_strcmp(comm, "PING") == 0)
1104 		p_ping(comm, from, ArgList);
1105 	else if (my_strcmp(comm, "TOPIC") == 0)
1106 		p_topic(comm, from, ArgList);
1107 	else if (my_strcmp(comm, "PONG") == 0)
1108 		p_pong(comm, from, ArgList);
1109 	else if (my_strcmp(comm, "INVITE") == 0)
1110 		p_invite(from, ArgList);
1111 	else if (my_strcmp(comm, "NICK") == 0)
1112 		p_nick(comm, from, ArgList);
1113 	else if (my_strcmp(comm, "KILL") == 0)
1114 		p_server_kill(from, ArgList);
1115 	else if (my_strcmp(comm, "MODE") == 0)
1116 		p_mode(comm, from, ArgList);
1117 	else if (my_strcmp(comm, "KICK") == 0)
1118 		p_kick(comm, from, ArgList);
1119 	else if (my_strcmp(comm, "ERROR") == 0)
1120 		p_error(from, ArgList);
1121 	else if (my_strcmp(comm, "ERROR:") == 0) /* Server bug makes this a must */
1122 		p_error(from, ArgList);
1123 	else
1124 		irc2_odd_server_stuff(comm, from, ArgList);
1125 out:
1126 	new_free(&copy);
1127 }
1128 
1129 int
doing_privmsg(void)1130 doing_privmsg(void)
1131 {
1132 	return doing_privmsg_active;
1133 }
1134 
1135 u_char *
from_user_host(void)1136 from_user_host(void)
1137 {
1138 	return FromUserHost;
1139 }
1140 
1141 void
set_from_user_host(u_char * fuh)1142 set_from_user_host(u_char *fuh)
1143 {
1144 	FromUserHost = fuh;
1145 }
1146 
1147 u_char *
get_joined_nick(void)1148 get_joined_nick(void)
1149 {
1150 	return joined_nick;
1151 }
1152 
1153 u_char *
get_public_nick(void)1154 get_public_nick(void)
1155 {
1156 	return public_nick;
1157 }
1158