1 /*
2  * parse.c: handles messages from the server.   Believe it or not.  I
3  * certainly wouldn't if I were you.
4  *
5  * Copyright (c) 1990 Michael Sandroff.
6  * Copyright (c) 1991, 1992 Troy Rollo.
7  * Copyright (c) 1992-1996 Matthew Green.
8  * Copyright 1997, 2003 EPIC Software Labs.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notices, the above paragraph (the one permitting redistribution),
18  *    this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The names of the author(s) may not be used to endorse or promote
21  *    products derived from this software without specific prior written
22  *    permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include "irc.h"
38 #include "server.h"
39 #include "names.h"
40 #include "vars.h"
41 #include "ctcp.h"
42 #include "hook.h"
43 #include "commands.h"
44 #include "ignore.h"
45 #include "lastlog.h"
46 #include "ircaux.h"
47 #include "sedcrypt.h"
48 #include "termx.h"
49 #include "flood.h"
50 #include "window.h"
51 #include "screen.h"
52 #include "output.h"
53 #include "numbers.h"
54 #include "parse.h"
55 #include "notify.h"
56 #include "timer.h"
57 
58 #define STRING_CHANNEL 	'+'
59 #define MULTI_CHANNEL 	'#'
60 #define LOCAL_CHANNEL 	'&'
61 #define ID_CHANNEL	'!'
62 
63 #define space 		' '	/* Taken from rfc 1459 */
64 #define	MAXPARA		20	/* RFC1459 says 15, but RusNet uses more */
65 
66 static	void	strip_modes (const char *, const char *, const char *);
67 
68 /* User and host information from server 2.7 */
69 const char	*FromUserHost = empty_string;
70 
71 /*
72  * is_channel: determines if the argument is a channel.  If it's a number,
73  * begins with MULTI_CHANNEL and has no '*', or STRING_CHANNEL, then its a
74  * channel
75  */
is_channel(const char * to)76 int 	is_channel (const char *to)
77 {
78 	const	char	*chantypes;
79 
80 	if (!to || !*to)
81 		return 0;
82 
83 	chantypes = get_server_005(from_server, "CHANTYPES");
84 	if (chantypes && *chantypes)
85 		return (!!strchr(chantypes, *to));
86 
87 	return ((*to == MULTI_CHANNEL) || (*to == STRING_CHANNEL) ||
88 		(*to == LOCAL_CHANNEL) || (*to == ID_CHANNEL));
89 }
90 
91 /*
92  * is_to_channel: determines if the argument is a channel target for
93  * privmsg/notice.  STATUSMSG can appear before CHANTYPES on 005 servers.
94  */
is_target_channel_wall(const char * to)95 static int	is_target_channel_wall (const char *to)
96 {
97 	const char *	statusmsg;
98 
99 	if (!to || !*to)
100 		return 0;
101 
102 	statusmsg = get_server_005(from_server, "STATUSMSG");
103 	if (statusmsg && strchr(statusmsg, to[0]))
104 		return 1;
105 	else
106 		return 0;
107 }
108 
109 
110 /*
111  * This function reverses the action of BreakArgs but only after a certain
112  * token.   You shall not call this function with an 'arg_list' that was
113  * not previously passed to BreakArgs.
114  */
PasteArgs(const char ** arg_list,int paste_point)115 const char *	PasteArgs (const char **arg_list, int paste_point)
116 {
117 	int	i;
118 	char	*ptr;
119 	size_t	len;
120 
121 	/*
122 	 * Make sure there are enough args to parse...
123 	 */
124 	for (i = 0; i < paste_point; i++)
125 		if (!arg_list[i] || !*arg_list[i])
126 			return NULL;		/* Not enough args */
127 
128 	/*
129 	 * Tokens are followed by one or more nul's.  We need to change
130 	 * ALL of those nuls back to spaces.  We don't know how many nuls
131 	 * there might be so we have to check for each one.
132 	 */
133 	for (i = paste_point; arg_list[i] && arg_list[i + 1]; i++)
134 	{
135 		/*
136 		 * arg_list is (const char **) to prevent OTHER people from
137 	 	 * modifying it underneath us.  But we own arg_list, so this
138 		 * laundering away of const is reasonable safe, and proper.
139 		 */
140 		ptr = (char *)
141 #ifdef HAVE_INTPTR_T
142 				(intptr_t)
143 #endif
144 					   arg_list[i];
145 
146 		/*
147 		 * Yes, this IS safe!  Please note above that the above for
148 		 * loop walks tokens (1, N-1), so we are NOT clobbering the
149 	 	 * actual final nul on the end of the original string.
150 		 * We leave the nul on the end of the final arg exactly the
151 		 * way that BreakArgs parsed it.
152 		 */
153 		len = strlen(ptr);
154 		while (ptr[len] == '\0')
155 			ptr[len++] = ' ';
156 	}
157 
158 	arg_list[paste_point + 1] = NULL;
159 	return arg_list[paste_point];
160 }
161 
162 /*
163  * BreakArgs: breaks up the line from the server, in to where its from,
164  * setting FromUserHost if it should be, and returns all the arguements
165  * that are there.   Re-written by phone, dec 1992.
166  *		     Re-written again by esl, april 1996.
167  *
168  * This doesnt strip out extraneous spaces any more.
169  */
BreakArgs(char * Input,const char ** Sender,const char ** OutPut)170 static void 	BreakArgs (char *Input, const char **Sender, const char **OutPut)
171 {
172 	int	ArgCount;
173 
174 	/*
175 	 * Paranoia.  Clean out any bogus ptrs still left on OutPut...
176 	 */
177 	for (ArgCount = 0; ArgCount <= MAXPARA + 1; ArgCount++)
178 		OutPut[ArgCount] = NULL;
179 	ArgCount = 0;
180 
181 	/*
182 	 * The RFC describes it fully, but in a short form, a line looks like:
183 	 * [:sender[!user@host]] COMMAND ARGUMENT [[:]ARGUMENT]{0..14}
184 	 */
185 
186 	/*
187 	 * Look to see if the optional :sender is present.
188 	 */
189 	if (*Input == ':')
190 	{
191 		char	*fuh;
192 
193 		fuh = ++Input;
194 		while (*Input && *Input != space)
195 			Input++;
196 		while (*Input == space)
197 			*Input++ = 0;
198 
199 		/*
200 		 * Look to see if the optional !user@host is present.
201 		 */
202 		*Sender = fuh;
203 		while (*fuh && *fuh != '!')
204 			fuh++;
205 		if (*fuh == '!')
206 			*fuh++ = 0;
207 		FromUserHost = fuh;
208 	}
209 	/*
210 	 * No sender present.
211 	 */
212 	else
213 		*Sender = FromUserHost = empty_string;
214 
215 	/*
216 	 * This changes all spaces (" ") in the protocol command to nuls ('\0')
217 	 * and puts pointers at the start of every token into OutPut[].  Note
218 	 * that if a token is followed by more than one space, they will be
219 	 * all changed into nul's.  The PasteArgs() function (above) handles
220 	 * that properly.
221 	 */
222 	for (;;)
223 	{
224 		if (!*Input)
225 			break;
226 
227 		if (*Input == ':')
228 		{
229 			/* Squash the : so if PasteArgs() is called it doesn't reappear */
230 			ov_strcpy(Input, Input + 1);
231 			OutPut[ArgCount++] = Input;
232 			break;
233 		}
234 
235 		OutPut[ArgCount++] = Input;
236 		if (ArgCount > MAXPARA)
237 			break;
238 
239 		while (*Input && *Input != space)
240 			Input++;
241 		while (*Input && *Input == space)
242 			*Input++ = 0;
243 	}
244 	OutPut[ArgCount] = NULL;
245 }
246 
247 /* in response to a TOPIC message from the server */
p_topic(const char * from,const char * comm,const char ** ArgList)248 static void	p_topic (const char *from, const char *comm, const char **ArgList)
249 {
250 	const char 	*channel, *new_topic;
251 	int	l;
252 
253 	if (!(channel = ArgList[0]))
254 		{ rfc1459_odd(from, comm, ArgList); return; }
255 	if (!(new_topic = ArgList[1]))
256 		{ rfc1459_odd(from, comm, ArgList); return; }
257 
258 	if (check_ignore_channel(from, FromUserHost,
259 				channel, LEVEL_TOPIC) == IGNORED)
260 		return;
261 
262 	if (new_check_flooding(from, FromUserHost,
263 				channel, new_topic, LEVEL_TOPIC))
264 		return;
265 
266 	l = message_from(channel, LEVEL_TOPIC);
267 	if (do_hook(TOPIC_LIST, "%s %s %s", from, channel, new_topic))
268 		say("%s has changed the topic on channel %s to %s",
269 			from, check_channel_type(channel), new_topic);
270 	pop_message_from(l);
271 }
272 
p_wallops(const char * from,const char * comm,const char ** ArgList)273 static void	p_wallops (const char *from, const char *comm, const char **ArgList)
274 {
275 	const char 	*message;
276 	int 	server_wallop;
277 	int	l;
278 
279 	if (!(message = ArgList[0]))
280 		{ rfc1459_odd(from, comm, ArgList); return; }
281 
282 	server_wallop = strchr(from, '.') ? 1 : 0;
283 
284 	/* So Black will stop asking me to add it... */
285 	if (!strncmp(message, "OPERWALL - ", 11))
286 	{
287 		int	retval;
288 
289 		/* Check for ignores... */
290 		if (check_ignore(from, FromUserHost, LEVEL_OPERWALL) == IGNORED)
291 			return;
292 
293 		/* Check for floods... servers are exempted from flood checks */
294 		if (!server_wallop && check_flooding(from, FromUserHost,
295 						LEVEL_OPERWALL, message))
296 			return;
297 
298 		l = message_from(NULL, LEVEL_OPERWALL);
299 		retval = do_hook(OPERWALL_LIST, "%s %s", from, message + 11);
300 		pop_message_from(l);
301 		if (!retval)
302 			return;
303 	}
304 
305 	/*
306 	 * If it's not an operwall, ,or if the user didn't catch it,
307 	 * treat it as a wallop.
308 	 */
309 
310 	/* Check for ignores... */
311 	if (check_ignore(from, FromUserHost, LEVEL_WALLOP) == IGNORED)
312 		return;
313 
314 	/* Check for floods... servers are exempted from flood checks */
315 	if (!server_wallop && check_flooding(from, FromUserHost,
316 						LEVEL_WALLOP, message))
317 		return;
318 
319 	l = message_from(NULL, LEVEL_WALLOP);
320 	if (do_hook(WALLOP_LIST, "%s %c %s",
321 				from,
322 				server_wallop ? 'S' : '*',
323 				message))
324 		put_it("!%s%s! %s",
325 				from, server_wallop ? empty_string : star,
326 				message);
327 	pop_message_from(l);
328 }
329 
330 /*
331  * p_privmsg - Handle PRIVMSG messages from irc server
332  *
333  * Arguments:
334  *	from	- The sender of the PRIVMSG (usually a nick; servers uncommon)
335  *	comm	- The actual protocol command ("PRIVMSG")
336  *	ArgList - Arguments to the PRIVMSG:
337  *	  ArgList[0] - The receiver of the message (nick or channel)
338  *	  ArgList[1] - Payload of the message
339  *
340  * Notes:
341  *   PRIVMSGs may be sent to
342  *	1. A nick (our nick)
343  *	2. A channel (that we're on)
344  *	3. A prefix + A channel (eg "@#channel" or "+#channel")
345  *	   We treat this as #2.
346  *	4. Something else (a wall, ie, "*.iastate.edu")
347  *
348  *   PRIVMSG are sorted into several piles:
349  *	"PUBLIC" level:
350  *	1. Someone on the channel sends a message to channel	-> PUBLIC
351  *	    + The channel is current channel in any window
352  *	2. Someone on the channel sends a message to channel	-> PUBLIC_OTHER
353  *	    + The channel is NOT a current channel on any window
354  *	3. Someone not on channel sends a message to channel	-> PUBLIC_MSG
355  *
356  *	"MSG" level:
357  *	4. A message sent to our nickname			-> MSG
358  *
359  *	"WALL" level:
360  *	5. A message sent to any other target			-> MSG_GROUP
361  *
362  *
363  *   Processing
364  *   ==========
365  *   CTCPs are delivered via PRIVMSGs, causing a rewrite of ArgList[1].
366  *	+ Most CTCPs are just removed (CTCP requests)
367  *	+ Some do in-place substitution (Encryption - ie, CTCP CAST5)
368  *	+ CTCP handling happens first, before anything below
369  *	+ CTCP handling does its own ignore, flood control, and throttling.
370  *
371  *   If the PRIVMSG contains nothing after CTCP handling, then stop.
372  *
373  *   First, IGNOREs are checked. (CTCPs do their own ignore handling)
374  *
375  *   If the PRIVMSG is encrypted, it will first be offered via
376  *	/ON ENCRYPTED_PRIVMSG no matter who sent it or to whom
377  *	it was sent.
378  *
379  *   Next, flood control is checked.
380  *
381  *   All PRIVMSGs are offered via /ON GENERAL_PRIVMSG,
382  *	no matter who sent it or to whom it was sent.
383  *
384  *   All PRIVMSGs are offered via their respective types (see above)
385  *
386  *   Otherwise, a default message is output.
387  *
388  *   Finally, /NOTIFY is checked.
389  */
p_privmsg(const char * from,const char * comm,const char ** ArgList)390 static void	p_privmsg (const char *from, const char *comm, const char **ArgList)
391 {
392 	const char	*real_target, *target, *message;
393 	int		hook_type,
394 			level;
395 	const char	*hook_format;
396 	const char	*flood_channel = NULL;
397 	int	l;
398 
399 	PasteArgs(ArgList, 1);
400 	if (!(target = ArgList[0]))
401 		{ rfc1459_odd(from, comm, ArgList); return; }
402 	if (!(message = ArgList[1]))
403 		{ rfc1459_odd(from, comm, ArgList); return; }
404 
405 	set_server_doing_privmsg(from_server, 1);
406 	sed = 0;
407 
408 	/*
409 	 * Do ctcp's first, and if there's nothing left, then dont
410 	 * go to all the work below.  Plus, we dont set message_from
411 	 * until we know there's other stuff besides the ctcp in the
412 	 * message, which keeps things going to the wrong window.
413 	 */
414 	message = do_ctcp(1, from, target, (char *)
415 #ifdef HAVE_INTPTR_T
416 					(intptr_t)
417 #endif
418 						message);
419 	if (!*message) {
420 		set_server_doing_privmsg(from_server, 0);
421 		return;
422 	}
423 
424 	/* If this is a @#chan or +#chan, ignore the @ or +. */
425 	real_target = target;
426 	if (is_target_channel_wall(target) &&
427 			im_on_channel(target + 1, from_server))
428 		target++;
429 
430 	/* ooops. cant just do is_channel(to) because of # walls... */
431 	if (is_channel(target) && im_on_channel(target, from_server))
432 	{
433 		level = LEVEL_PUBLIC;
434 		flood_channel = target;
435 
436 		if (!is_channel_nomsgs(target, from_server) &&
437 				!is_on_channel(target, from)) {
438 			hook_type = PUBLIC_MSG_LIST;
439 			hook_format = "(%s/%s) %s";
440 		} else if (is_current_channel(target, from_server)) {
441 			hook_type = PUBLIC_LIST;
442 			hook_format = "<%s%.0s> %s";
443 		} else if (target != real_target &&
444 				is_current_channel(target, from_server)) {
445 			hook_type = PUBLIC_LIST;
446 			hook_format = "<%s:%s> %s";
447 		} else {
448 			hook_type = PUBLIC_OTHER_LIST;
449 			hook_format = "<%s:%s> %s";
450 		}
451 	}
452 	else if (!is_me(from_server, target))
453 	{
454 		level = LEVEL_WALL;
455 		flood_channel = NULL;
456 
457 		hook_type = MSG_GROUP_LIST;
458 		hook_format = "<-%s:%s-> %s";
459 	}
460 	else
461 	{
462 		level = LEVEL_MSG;
463 		flood_channel = NULL;
464 
465 		hook_type = MSG_LIST;
466 		hook_format = NULL;	/* See below */
467 		target = NULL;		/* Target is the sender */
468 	}
469 
470 	if (!target || !*target)
471 		target = from;		/* Target is actually sender here */
472 
473 	if (check_ignore_channel(from, FromUserHost, target, level) == IGNORED
474          || (check_ignore_channel(from, FromUserHost, real_target, level) == IGNORED))
475 	{
476 		set_server_doing_privmsg(from_server, 0);
477 		return;
478 	}
479 
480 	/* Encrypted privmsgs are specifically exempted from flood control */
481 	if (sed)
482 	{
483 		int	do_return = 1;
484 
485 		sed = 0;
486 		l = message_from(target, level);
487 		if (do_hook(ENCRYPTED_PRIVMSG_LIST, "%s %s %s",
488 					from, real_target, message))
489 			do_return = 0;
490 		pop_message_from(l);
491 
492 		if (do_return) {
493 			set_server_doing_privmsg(from_server, 0);
494 			return;
495 		}
496 	}
497 
498 	if (new_check_flooding(from, FromUserHost, flood_channel,
499 				message, level)) {
500 		set_server_doing_privmsg(from_server, 0);
501 		return;
502 	}
503 
504 	/* Control the "last public/private nickname" variables */
505 	if (hook_type == PUBLIC_LIST ||
506 	    hook_type == PUBLIC_MSG_LIST ||
507 	    hook_type == PUBLIC_OTHER_LIST)
508 		set_server_public_nick(from_server, from);
509 	else if (hook_type == MSG_LIST)
510 		set_server_recv_nick(from_server, from);
511 
512 	/* Go ahead and throw it to the user */
513 	l = message_from(target, level);
514 
515 	if (do_hook(GENERAL_PRIVMSG_LIST, "%s %s %s", from, real_target, message))
516 	{
517 	    if (hook_type == MSG_LIST)
518 	    {
519 		const char *away = get_server_away(NOSERV);
520 
521 		if (do_hook(hook_type, "%s %s", from, message))
522 		{
523 		    if (away)
524 			put_it("*%s* %s <%.16s>", from, message, my_ctime(time(NULL)));
525 		    else
526 			put_it("*%s* %s", from, message);
527 		}
528 	    }
529 
530 	    else if (do_hook(hook_type, "%s %s %s", from, real_target, message))
531 		put_it(hook_format, from, check_channel_type(real_target), message);
532 	}
533 
534 	/* Clean up and go home. */
535 	pop_message_from(l);
536 	set_server_doing_privmsg(from_server, 0);
537 
538 	/* Alas, this is not protected by protocol enforcement. :( */
539 	notify_mark(from_server, from, 1, 0);
540 }
541 
p_quit(const char * from,const char * comm,const char ** ArgList)542 static void	p_quit (const char *from, const char *comm, const char **ArgList)
543 {
544 	const char *	quit_message;
545 	int		one_prints = 1;
546 	const char *	chan;
547 	int		l;
548 
549 	if (!(quit_message = ArgList[0]))
550 		{ rfc1459_odd(from, comm, ArgList); return; }
551 
552 	/*
553 	 * Normally, we do not throw the user a hook until after we
554 	 * have taken care of administrative details.  But in this case,
555 	 * someone has QUIT but the user may want their user@host info
556 	 * so we cannot remove them from the channel until after we have
557 	 * thrown the hook.  That is the only reason this is out of order.
558 	 */
559 	if (check_ignore(from, FromUserHost, LEVEL_QUIT) == IGNORED)
560 		goto remove_quitter;
561 
562 	if (check_flooding(from, FromUserHost, LEVEL_QUIT, quit_message))
563 		goto remove_quitter;
564 
565 	for (chan = walk_channels(1, from); chan; chan = walk_channels(0, from))
566 	{
567 	    if (check_ignore_channel(from, FromUserHost,
568 					chan, LEVEL_QUIT) == IGNORED)
569 	    {
570 		one_prints = 0;
571 		continue;
572 	    }
573 
574 	    l = message_from(chan, LEVEL_QUIT);
575 	    if (!do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", chan, from,
576 							quit_message))
577 		one_prints = 0;
578 	    pop_message_from(l);
579 	}
580 
581 	if (one_prints)
582 	{
583 		l = message_from(what_channel(from, from_server), LEVEL_QUIT);
584 		if (do_hook(SIGNOFF_LIST, "%s %s", from, quit_message))
585 			say("Signoff: %s (%s)", from, quit_message);
586 		pop_message_from(l);
587 	}
588 
589 	/*
590 	 * This is purely ergonomic.  If the user is ignoring this person
591 	 * then if we tell the user that this person is offline as soon as
592 	 * we get the QUIT, this will leak to the user that the person was
593 	 * on the channel, thus defeating the ignore.  Best to just wait
594 	 * until the top of the next minute.
595 	 */
596 	notify_mark(from_server, from, 0, 0);
597 
598 remove_quitter:
599 	/* Send all data about this unperson to the memory hole. */
600 	remove_from_channel(NULL, from, from_server);
601 }
602 
p_pong(const char * from,const char * comm,const char ** ArgList)603 static void	p_pong (const char *from, const char *comm, const char **ArgList)
604 {
605 	const char *	pong_server, *pong_message;
606 	int	server_pong = 0;
607 
608 	PasteArgs(ArgList, 1);
609 	if (!(pong_server = ArgList[0]))
610 		{ rfc1459_odd(from, comm, ArgList); return; }
611 	if (!(pong_message = ArgList[1]))
612 		{ rfc1459_odd(from, comm, ArgList); return; }
613 
614 	if (strchr(pong_server, '.'))
615 		server_pong = 1;
616 
617 	/*
618 	 * In theory, we could try to use PING/PONG messages to
619 	 * do /redirect and /wait, but we don't.  When the day comes
620 	 * that we do, this code will do that for us.
621 	 */
622 	if (!my_stricmp(from, get_server_itsname(from_server)))
623 	{
624 		if (check_server_redirect(from_server, pong_message))
625 			return;
626 		if (check_server_wait(from_server, pong_message))
627 			return;
628 	}
629 
630 	if (check_ignore(from, FromUserHost, LEVEL_OTHER) == IGNORED)
631 		return;
632 
633 	if (do_hook(PONG_LIST, "%s %s %s", from, pong_server, pong_message))
634 	    if (server_pong)
635 		say("%s: PONG received from %s (%s)", pong_server,
636 							from, pong_message);
637 }
638 
p_error(const char * from,const char * comm,const char ** ArgList)639 static void	p_error (const char *from, const char *comm, const char **ArgList)
640 {
641 	const char *	the_error;
642 
643 	PasteArgs(ArgList, 0);
644 	if (!(the_error = ArgList[0]))
645 		{ rfc1459_odd(from, comm, ArgList); return; }
646 
647 	if (!from || !isgraph(*from))
648 		from = star;
649 
650 	if (do_hook(ERROR_LIST, "%s %s", from, the_error))
651 		say("%s %s", from, the_error);
652 }
653 
p_channel(const char * from,const char * comm,const char ** ArgList)654 static void	p_channel (const char *from, const char *comm, const char **ArgList)
655 {
656 	const char	*channel;
657 	char 	*c;
658 	int 	op = 0, vo = 0, ha = 0;
659 	char 	extra[20];
660 	int	l;
661 
662 	/* We cannot join channel 0 */
663 	if (!(channel = ArgList[0]))
664 		{ rfc1459_odd(from, comm, ArgList); return; }
665 	if (!strcmp(channel, zero))
666 		{ rfc1459_odd(from, comm, ArgList); return; }
667 
668 	/*
669 	 * Workaround for extremely gratuitous protocol change in av2.9
670 	 */
671 	if ((c = strchr(channel, '\007')))
672 	{
673 		for (*c++ = 0; *c; c++)
674 		{
675 			     if (*c == 'o') op = 1;
676 			else if (*c == 'v') vo = 1;
677 		}
678 	}
679 
680 	if (is_me(from_server, from))
681 	{
682 		add_channel(channel, from_server);
683 		send_to_server("MODE %s", channel);
684 	}
685 	else
686 	{
687 		add_to_channel(channel, from, from_server, 0, op, vo, ha);
688 		add_userhost_to_channel(channel, from, from_server, FromUserHost);
689 	}
690 
691 	if (check_ignore_channel(from, FromUserHost,
692 				channel, LEVEL_JOIN) == IGNORED)
693 		return;
694 
695 	if (new_check_flooding(from, FromUserHost, channel, star, LEVEL_JOIN))
696 		return;
697 
698 	set_server_joined_nick(from_server, from);
699 
700 	*extra = 0;
701 	if (op)
702 		strlcat(extra, " (+o)", sizeof extra);
703 	if (vo)
704 		strlcat(extra, " (+v)", sizeof extra);
705 
706 	l = message_from(channel, LEVEL_JOIN);
707 	if (do_hook(JOIN_LIST, "%s %s %s %s",
708 			from, channel, FromUserHost, extra))
709 		say("%s (%s) has joined channel %s%s",
710 			from, FromUserHost,
711 			check_channel_type(channel), extra);
712 	pop_message_from(l);
713 
714 	/*
715 	 * The placement of this is purely ergonomic.  The user might
716 	 * be alarmed if epic thrown an /on notify_signon before it
717 	 * throws the /on join that triggers it.  Plus, if the user is
718 	 * ignoring this person (nothing says you can't ignore someone
719 	 * who is on your notify list), then it would also not be the
720 	 * best idea to throw /on notify_signon as a result of an
721 	 * /on join since that would leak to the user that the person
722 	 * has joined the channel -- best to just leave the notify stuff
723 	 * alone until the top of the next minute.
724 	 */
725 	notify_mark(from_server, from, 1, 0);
726 }
727 
p_invite(const char * from,const char * comm,const char ** ArgList)728 static void 	p_invite (const char *from, const char *comm, const char **ArgList)
729 {
730 	const char	*invitee, *invited_to;
731 	int	l;
732 
733 	if (!(invitee = ArgList[0]))
734 		{ rfc1459_odd(from, comm, ArgList); return; }
735 	if (!(invited_to = ArgList[1]))
736 		{ rfc1459_odd(from, comm, ArgList); return; }
737 
738 	if (check_ignore_channel(from, FromUserHost,
739 				invited_to, LEVEL_INVITE) == IGNORED)
740 		return;
741 
742 	if (check_flooding(from, FromUserHost, LEVEL_INVITE, invited_to))
743 		return;
744 
745 	set_server_invite_channel(from_server, invited_to);
746 	set_server_recv_nick(from_server, from);
747 
748 	l = message_from(from, LEVEL_INVITE);
749 	if (do_hook(INVITE_LIST, "%s %s %s", from, invited_to, FromUserHost))
750 		say("%s (%s) invites you to channel %s",
751 			from, FromUserHost, invited_to);
752 	pop_message_from(l);
753 }
754 
755 /*
756  * Received whenever we have been killed.
757  * (On old MS COMIC CHAT servers, also when someone else was killed)
758  *
759  * Up through epic4, there used to be a /set auto_reconnect which was
760  * used here, but epic5 uses server states, so p_kill is now treated
761  * as an advisory message (your script pack decides what to do once you
762  * actually get the EOF from the server).
763  *
764  * All we do now is throw /on disconnect and/or tell you what
765  * happened and it's someone else's problem what to do with that.
766  */
p_kill(const char * from,const char * comm,const char ** ArgList)767 static void	p_kill (const char *from, const char *comm, const char **ArgList)
768 {
769 	const char 	*victim, *reason;
770 	int 	hooked;
771 
772 	if (!(victim = ArgList[0]))
773 		{ rfc1459_odd(from, comm, ArgList); return; }
774 	if (!(reason = ArgList[1])) { }
775 
776 	/*
777 	 * Old MS Comic Chat servers (exchange) sent a KILL instead of
778 	 * a QUIT when someone was killed.  We reroute that to QUIT.
779 	 */
780 	if (!is_me(from_server, victim))
781 	{
782 		p_quit(from, comm, ArgList);
783 		return;
784 	}
785 
786 
787 	/*
788 	 * If we've been killed by our server, we need take no action
789 	 * because when we are dropped by the server, we will take this as
790 	 * any other case where we recieve abnormal termination from the
791 	 * server and we will reconnect and rejoin.
792 	 */
793 	if (strchr(from, '.'))
794         {
795 		if (!reason) { reason = "Probably due to a nick collision"; }
796 
797 		say("Server [%s] has rejected you. (%s)", from, reason);
798 		return;
799 	}
800 
801 
802 	/*
803 	 * We've been killed.  Bummer for us.
804 	 */
805 	if (!reason) { reason = "No Reason Given"; }
806 
807 	if ((hooked = do_hook(DISCONNECT_LIST, "Killed by %s (%s)",
808 						from, reason)))
809 	{
810 		say("You have been killed by that fascist [%s] %s",
811 						from, reason);
812 	}
813 
814 
815 	/*
816 	 * If we are a bot, and /on disconnect didnt hook,
817 	 * then we arent going anywhere.  We might as well quit.
818 	 */
819 	if (background && !hooked)
820 		irc_exit(1, NULL);
821 }
822 
p_ping(const char * from,const char * comm,const char ** ArgList)823 static void	p_ping (const char *from, const char *comm, const char **ArgList)
824 {
825 	const char *	message;
826 
827         PasteArgs(ArgList, 0);
828 	if (!(message = ArgList[0]))
829 		{ rfc1459_odd(from, comm, ArgList); return; }
830 
831 	send_to_server("PONG %s", message);
832 }
833 
p_silence(const char * from,const char * comm,const char ** ArgList)834 static void	p_silence (const char *from, const char *comm, const char **ArgList)
835 {
836 	const char *target;
837 	char mag;
838 
839 	if (!(target = ArgList[0]))
840 		{ rfc1459_odd(from, comm, ArgList); return; }
841 
842 	mag = *target++;
843 	if (do_hook(SILENCE_LIST, "%c %s", mag, target))
844 	{
845 		if (mag == '+')
846 			say ("You will no longer receive msgs from %s", target);
847 		else if (mag == '-')
848 			say ("You will now recieve msgs from %s", target);
849 		else
850 			say ("Unrecognized silence argument: %s", target);
851 	}
852 }
853 
p_nick(const char * from,const char * comm,const char ** ArgList)854 static void	p_nick (const char *from, const char *comm, const char **ArgList)
855 {
856 	const char	*new_nick;
857 	int		been_hooked = 0,
858 			its_me = 0;
859 	const char	*chan;
860 	int		ignored = 0;
861 	int		l;
862 
863 	if (!(new_nick = ArgList[0]))
864 		{ rfc1459_odd(from, comm, ArgList); return; }
865 
866 	/*
867 	 * Is this me changing nick?
868 	 */
869 	if (is_me(from_server, from))
870 	{
871 		its_me = 1;
872 		accept_server_nickname(from_server, new_nick);
873 	}
874 
875 	if (check_ignore(from, FromUserHost, LEVEL_NICK) == IGNORED)
876 		goto do_rename;
877 
878 	if (check_flooding(from, FromUserHost, LEVEL_NICK, new_nick))
879 		goto do_rename;
880 
881 	for (chan = walk_channels(1, from); chan; chan = walk_channels(0, from))
882 	{
883 		if (check_ignore_channel(from, FromUserHost, chan,
884 						LEVEL_NICK) == IGNORED)
885 		{
886 			ignored = 1;
887 			continue;
888 		}
889 
890 		l = message_from(chan, LEVEL_NICK);
891 		if (!do_hook(CHANNEL_NICK_LIST, "%s %s %s", chan, from, new_nick))
892 			been_hooked = 1;
893 		pop_message_from(l);
894 	}
895 
896 	if (!been_hooked && !ignored)
897 	{
898 		if (its_me)
899 			l = message_from(NULL, LEVEL_NICK);
900 		else
901 			l = message_from(what_channel(from, from_server),
902 						LEVEL_NICK);
903 
904 		if (do_hook(NICKNAME_LIST, "%s %s", from, new_nick))
905 			say("%s is now known as %s", from, new_nick);
906 
907 		pop_message_from(l);
908 	}
909 
910 do_rename:
911 	notify_mark(from_server, from, 0, 0);
912 	rename_nick(from, new_nick, from_server);
913 	notify_mark(from_server, new_nick, 1, 0);
914 }
915 
p_mode(const char * from,const char * comm,const char ** ArgList)916 static void	p_mode (const char *from, const char *comm, const char **ArgList)
917 {
918 	const char	*target, *changes;
919 	const char	*m_target;
920 	const char	*type;
921 	int		l;
922 
923 	while (ArgList[0] && !*ArgList[0])
924 		++ArgList;			 /* Ride Austhex breakage */
925 	PasteArgs(ArgList, 1);
926 	if (!(target = ArgList[0]))
927 		{ rfc1459_odd(from, comm, ArgList); return; }
928 	if (!(changes = ArgList[1])) { return; } /* Ignore UnrealIRCD */
929 	if (!*changes) { return; }		 /* Ignore UnrealIRCD */
930 
931 	if (get_int_var(MODE_STRIPPER_VAR))
932 		strip_modes(from, target, changes);
933 
934 	if (is_channel(target))
935 	{
936 		m_target = target;
937 		target = check_channel_type(target);
938 		type = "on channel";
939 	}
940 	else
941 	{
942 		m_target = NULL;
943 		type = "for user";
944 	}
945 
946 	if (check_ignore_channel(from, FromUserHost,
947 					target, LEVEL_MODE) == IGNORED)
948 		goto do_update_mode;
949 
950 	if (new_check_flooding(from, FromUserHost, target, changes, LEVEL_MODE))
951 		goto do_update_mode;
952 
953 	l = message_from(m_target, LEVEL_MODE);
954 	if (do_hook(MODE_LIST, "%s %s %s", from, target, changes))
955 	    say("Mode change \"%s\" %s %s by %s",
956 					changes, type, target, from);
957 	pop_message_from(l);
958 
959 do_update_mode:
960 	if (is_channel(target))
961 		update_channel_mode(target, changes);
962 	else
963 		update_user_mode(from_server, changes);
964 }
965 
strip_modes(const char * from,const char * channel,const char * line)966 static void strip_modes (const char *from, const char *channel, const char *line)
967 {
968 	char	*mode;
969 	char 	*pointer;
970 	char	mag = '+'; /* XXXX Bogus */
971         char    *copy = (char *) 0;
972 	char	*free_copy;
973 	int	l;
974 
975 	free_copy = LOCAL_COPY(line);
976 	copy = free_copy;
977 	mode = next_arg(copy, &copy);
978 
979 	if (is_channel(channel))
980 	{
981 	    l = message_from(channel, LEVEL_MODE);
982 
983 	    for (pointer = mode; *pointer; pointer++)
984 	    {
985 		char	c = *pointer;
986 		char	*arg = NULL;
987 
988 		switch (chanmodetype(c))
989 		{
990 			case 1:
991 				mag = c;
992 				continue;
993 			case 6:
994 				break;
995 			case 5:
996 				if (mag == '-')
997 					break;
998 				FALLTHROUGH
999 			case 4: case 3: case 2:
1000 				if (!(arg = next_arg(copy, &copy)))
1001 					arg = endstr(copy);
1002 				break;
1003 			default:
1004 				/* We already get a yell from decifer_mode() */
1005 				break;
1006 		}
1007 		if (arg)
1008 			do_hook(MODE_STRIPPED_LIST,
1009 				"%s %s %c%c %s",
1010 				from, channel, mag,
1011 				c,arg);
1012 		else
1013 			do_hook(MODE_STRIPPED_LIST,
1014 				"%s %s %c%c",
1015 				from,channel,mag,c);
1016 	    }
1017 
1018 	    pop_message_from(l);
1019 	}
1020 
1021 	else /* User mode */
1022 	{
1023 	    l = message_from(NULL, LEVEL_MODE);
1024 
1025 	    for (pointer = mode; *pointer; pointer++)
1026 	    {
1027 		char	c = *pointer;
1028 
1029 		switch (c)
1030 		{
1031 			case '+' :
1032 			case '-' : mag = c; break;
1033 			default  :
1034 				do_hook(MODE_STRIPPED_LIST,
1035 					"%s %s %c%c",
1036 					from, channel, mag, c);
1037 				break;
1038 		}
1039 	    }
1040 
1041 	    pop_message_from(l);
1042 	}
1043 
1044 }
1045 
p_kick(const char * from,const char * comm,const char ** ArgList)1046 static void	p_kick (const char *from, const char *comm, const char **ArgList)
1047 {
1048 	const char	*channel, *victim, *comment;
1049 	int	l;
1050 
1051 	if (!(channel = ArgList[0]))
1052 		{ rfc1459_odd(from, comm, ArgList); return; }
1053 	if (!(victim = ArgList[1]))
1054 		{ rfc1459_odd(from, comm, ArgList); return; }
1055 	if (!(comment = ArgList[2])) { comment = "(no comment)"; }
1056 
1057 
1058 	/*
1059 	 * All this to handle being kicked...
1060 	 */
1061 	if (is_me(-1, victim))
1062 	{
1063 		Window *win, *old_cw;
1064 
1065 		/*
1066 		 * Uh-oh.  If win is null we have a problem.
1067 		 */
1068 		if (!(win = get_window_by_refnum(
1069 				get_channel_winref(channel, from_server))))
1070 		{
1071 		    /*
1072 		     * Check to see if we got a KICK for a
1073 		     * channel we dont think we're on.
1074 		     */
1075 		    if (im_on_channel(channel, from_server))
1076 			panic(0, "Window is NULL for channel [%s]", channel);
1077 
1078 		    yell("You were KICKed by [%s] on channel [%s] "
1079 			 "(reason [%s]), which you are not on!  "
1080 			 "Will not try to auto-rejoin",
1081 				from, channel, comment);
1082 
1083 		    return;
1084 		}
1085 
1086 		/* XXX A POX ON ANYONE WHO ASKS ME TO MOVE THIS AGAIN XXX */
1087 		old_cw = current_window;
1088 		current_window = win;
1089 		l = message_setall(win->refnum, channel, LEVEL_KICK);
1090 
1091 		if (do_hook(KICK_LIST, "%s %s %s %s", victim, from,
1092 					check_channel_type(channel), comment))
1093 			say("You have been kicked off channel %s by %s (%s)",
1094 					check_channel_type(channel), from,
1095 					comment);
1096 
1097 		pop_message_from(l);
1098 		current_window = old_cw;
1099 
1100 		remove_channel(channel, from_server);
1101 		update_all_status();
1102 		return;
1103 	}
1104 
1105 	if (check_ignore_channel(from, FromUserHost,
1106 				channel, LEVEL_KICK) == IGNORED)
1107 		goto do_remove_nick;
1108 
1109 	if (check_ignore_channel(victim, fetch_userhost(from_server, NULL,
1110 							victim),
1111 					channel, LEVEL_KICK) == IGNORED)
1112 		goto do_remove_nick;
1113 
1114 
1115 	if (new_check_flooding(from, FromUserHost, channel, victim, LEVEL_KICK))
1116 		goto do_remove_nick;
1117 
1118 	l = message_from(channel, LEVEL_KICK);
1119 	if (do_hook(KICK_LIST, "%s %s %s %s",
1120 			victim, from, channel, comment))
1121 		say("%s has been kicked off channel %s by %s (%s)",
1122 			victim, check_channel_type(channel), from, comment);
1123 	pop_message_from(l);
1124 
1125 do_remove_nick:
1126 	/*
1127 	 * The placement of this is purely ergonomic.  When someone is
1128 	 * kicked, the user may want to know what their userhost was so
1129 	 * they can take whatever appropriate action is called for.  This
1130 	 * requires that the user still be considered "on channel" in the
1131 	 * /on kick, even though the user has departed.
1132 	 *
1133 	 * Send all data for this unperson to the memory hole.
1134 	 */
1135 	remove_from_channel(channel, victim, from_server);
1136 }
1137 
p_part(const char * from,const char * comm,const char ** ArgList)1138 static void	p_part (const char *from, const char *comm, const char **ArgList)
1139 {
1140 	const char	*channel, *reason;
1141 	int	l;
1142 
1143 	PasteArgs(ArgList, 1);
1144 	if (!(channel = ArgList[0]))
1145 		{ rfc1459_odd(from, comm, ArgList); return; }
1146 	if (!(reason = ArgList[1])) { }
1147 
1148 	if ((check_ignore_channel(from, FromUserHost,
1149 				channel, LEVEL_PART) != IGNORED)
1150 		&& !new_check_flooding(from, FromUserHost, channel,
1151 			reason ? reason : star, LEVEL_PART))
1152 	{
1153 		l = message_from(channel, LEVEL_PART);
1154 		if (reason)		/* Dalnet part messages */
1155 		{
1156 			if (do_hook(PART_LIST, "%s %s %s %s",
1157 				from, channel, FromUserHost, reason))
1158 			    say("%s has left channel %s because (%s)",
1159 				from, check_channel_type(channel), reason);
1160 		}
1161 		else
1162 		{
1163 			if (do_hook(PART_LIST, "%s %s %s",
1164 				from, channel, FromUserHost))
1165 			    say("%s has left channel %s",
1166 				from, check_channel_type(channel));
1167 		}
1168 		pop_message_from(l);
1169 	}
1170 
1171 	if (is_me(from_server, from))
1172 		remove_channel(channel, from_server);
1173 	else
1174 		remove_from_channel(channel, from, from_server);
1175 
1176 }
1177 
1178 /*
1179  * Egads. i hope this is right.
1180  */
p_rpong(const char * from,const char * comm,const char ** ArgList)1181 static void	p_rpong (const char *from, const char *comm, const char **ArgList)
1182 {
1183 	const char *	nick, *target_server, *millisecs, *orig_time;
1184 	time_t delay;
1185 
1186 	if (!(nick = ArgList[0]))
1187 		{ rfc1459_odd(from, comm, ArgList); return; }
1188 	if (!(target_server = ArgList[1]))
1189 		{ rfc1459_odd(from, comm, ArgList); return; }
1190 	if (!(millisecs = ArgList[2]))
1191 		{ rfc1459_odd(from, comm, ArgList); return; }
1192 	if (!(orig_time = ArgList[3]))
1193 		{ rfc1459_odd(from, comm, ArgList); return; }
1194 
1195 	/*
1196 	 * :server RPONG yournick remoteserv ms :yourargs
1197 	 *
1198 	 * ArgList[0] -- our nickname (presumably)
1199 	 * ArgList[1] -- The server we RPING'd
1200 	 * ArgList[2] -- The number of ms it took to return
1201 	 * ArgList[3] -- The arguments we passed (presumably)
1202 	 */
1203 
1204 	delay = time(NULL) - atol(orig_time);
1205 	say("Pingtime %s - %s : %s ms (total delay: "INTMAX_FORMAT" s)",
1206 		from, target_server, millisecs, (intmax_t)delay);
1207 }
1208 
1209 /*
1210  * This is a special subset of server (OPER) notice.
1211  */
p_killmsg(const char * from,const char * to,const char * cline)1212 static int 	p_killmsg (const char *from, const char *to, const char *cline)
1213 {
1214 	char *poor_sap;
1215 	char *bastard;
1216 	const char *path_to_bastard;
1217 	const char *reason;
1218 	char *line;
1219 	int   l, retval;
1220 
1221 	l = message_from(to, LEVEL_OPNOTE);
1222 	line = LOCAL_COPY(cline);
1223 	if (!(poor_sap = next_arg(line, &line)))
1224 		return 0;		/* MALFORMED - IGNORED */
1225 
1226 	/* Dalnet kill BBC and doesnt append the period */
1227 	if (!end_strcmp(poor_sap, ".", 1))
1228 		chop(poor_sap, 1);
1229 
1230 	/* dalnet kill BBC and doesnt use "From", but "from" */
1231 	if (my_strnicmp(line, "From ", 5))
1232 	{
1233 		yell("Attempted to parse an ill-formed KILL request [%s %s]",
1234 			poor_sap, line);
1235 		pop_message_from(l);
1236 		return 0;
1237 	}
1238 	line += 5;
1239 	bastard = next_arg(line, &line);
1240 
1241 	/* Hybrid BBC and doesn't include the kill-path. */
1242 	/* Fend off future BBC kills */
1243 	if (my_strnicmp(line, "Path: ", 6))
1244 	{
1245 		path_to_bastard = "*";
1246 		reason = line;		/* Hope for the best */
1247 	}
1248 	else
1249 	{
1250 		line += 6;
1251 		path_to_bastard = next_arg(line, &line);
1252 		reason = line;
1253 	}
1254 
1255 	retval = do_hook(KILL_LIST, "%s %s %s %s %s", from, poor_sap, bastard,
1256 					path_to_bastard, reason);
1257 	pop_message_from(l);
1258 	return !retval;
1259 }
1260 
1261 
1262 /*
1263  * This is a special subset of NOTICEs, that were sent from the server
1264  * we are connected to (not a remote server), to us (not to a channel).
1265  */
p_snotice(const char * from,const char * to,const char * line)1266 static 	void 	p_snotice (const char *from, const char *to, const char *line)
1267 {
1268 	const char *	f;
1269 	int	l;
1270 	int	retval;
1271 
1272 	f = from;
1273 	if (!f || !*f)
1274 		if (!(f = get_server_itsname(from_server)))
1275 			f = get_server_name(from_server);
1276 
1277 	/* OPERator Notices */
1278 	if (!strncmp(line, "*** Notice -- ", 13))
1279 	{
1280 		if (!strncmp(line + 14, "Received KILL message for ", 26))
1281 		{
1282 			if (p_killmsg(f, to, line + 40))
1283 				return;
1284 		}
1285 
1286 		l = message_from(to, LEVEL_OPNOTE);
1287 		retval = do_hook(OPER_NOTICE_LIST, "%s %s", f, line + 14);
1288 		pop_message_from(l);
1289 		if (!retval)
1290 			return;
1291 	}
1292 
1293 	l = message_from(to, LEVEL_SNOTE);
1294 
1295 	/* Check to see if the notice already has its own header... */
1296 	if (do_hook(GENERAL_NOTICE_LIST, "%s %s %s", f, to, line))
1297 	{
1298 	    if (*line == '*' || *line == '#')
1299 	    {
1300 		if (do_hook(SERVER_NOTICE_LIST, "%s %s", f, line))
1301 			put_it("%s", line);
1302 	    }
1303 	    else
1304 		if (do_hook(SERVER_NOTICE_LIST, "%s *** %s", f, line))
1305 			say("%s", line);
1306 	}
1307 
1308 	pop_message_from(l);
1309 }
1310 
1311 /*
1312  * The main handler for those wacky NOTICE commands...
1313  * This is as much like p_privmsg as i can get away with.
1314  */
p_notice(const char * from,const char * comm,const char ** ArgList)1315 static void 	p_notice (const char *from, const char *comm, const char **ArgList)
1316 {
1317 	const char 	*target, *message;
1318 	const char	*real_target;
1319 	int		hook_type;
1320 	const char *	flood_channel = NULL;
1321 	int		l;
1322 
1323 	PasteArgs(ArgList, 1);
1324 	if (!(target = ArgList[0]))
1325 		{ rfc1459_odd(from, comm, ArgList); return; }
1326 	if (!(message = ArgList[1]))
1327 		{ rfc1459_odd(from, comm, ArgList); return; }
1328 
1329 	set_server_doing_notice(from_server, 1);
1330 	sed = 0;
1331 
1332 	/* Do normal /CTCP reply handling */
1333 	/* XXX -- Casting "message" to (char *) is cheating. */
1334 	message = do_ctcp(0, from, target, (char *)
1335 #ifdef HAVE_INTPTR_T
1336 							(intptr_t)
1337 #endif
1338 								message);
1339 	if (!*message) {
1340 		new_check_flooding(from, FromUserHost, flood_channel,
1341 					message, LEVEL_NOTICE);
1342 		set_server_doing_notice(from_server, 0);
1343 		return;
1344 	}
1345 
1346 	/* If this is a @#chan or +#chan, ignore the @ or +. */
1347 	real_target = target;
1348 	if (is_target_channel_wall(target) &&
1349 			im_on_channel(target + 1, from_server))
1350 		target++;
1351 
1352 	/* For pesky prefix-less NOTICEs substitute the server's name */
1353 	/* Check to see if it is a "Server Notice" */
1354 	if (!from || !*from || !strcmp(get_server_itsname(from_server), from))
1355 	{
1356 		p_snotice(from, target, message);
1357 		set_server_doing_notice(from_server, 0);
1358 		return;
1359 	}
1360 
1361 	/*
1362 	 * Note that NOTICEs from servers are not "server notices" unless
1363 	 * the target is not a channel (ie, it is sent to us).  Any notice
1364 	 * that is sent to a channel is a normal NOTICE, notwithstanding
1365 	 * _who_ sent it.
1366 	 */
1367 	if (is_channel(target) && im_on_channel(target, from_server))
1368 	{
1369 		flood_channel = target;
1370 		hook_type = PUBLIC_NOTICE_LIST;
1371 	}
1372 	else if (!is_me(from_server, target))
1373 	{
1374 		flood_channel = NULL;
1375 		hook_type = NOTICE_LIST;
1376 	}
1377 	else
1378 	{
1379 		flood_channel = NULL;
1380 		hook_type = NOTICE_LIST;
1381 		target = from;
1382 	}
1383 
1384 	/* Check for /ignore's */
1385 	if (check_ignore_channel(from, FromUserHost,
1386 				target, LEVEL_NOTICE) == IGNORED)
1387 	{
1388 		set_server_doing_notice(from_server, 0);
1389 		return;
1390 	}
1391 
1392 	/* Let the user know if it is an encrypted notice */
1393 	/* Note that this is always hooked, even during a flood */
1394 	if (sed)
1395 	{
1396 		int	do_return = 1;
1397 
1398 		sed = 0;
1399 		l = message_from(target, LEVEL_NOTICE);
1400 
1401 		if (do_hook(ENCRYPTED_NOTICE_LIST, "%s %s %s",
1402 				from, real_target, message))
1403 			do_return = 0;
1404 
1405 		pop_message_from(l);
1406 
1407 		if (do_return) {
1408 			set_server_doing_notice(from_server, 0);
1409 			return;
1410 		}
1411 	}
1412 
1413 	if (new_check_flooding(from, FromUserHost, flood_channel,
1414 					message, LEVEL_NOTICE)) {
1415 		set_server_doing_notice(from_server, 0);
1416 		return;
1417 	}
1418 
1419 
1420 	/* Go ahead and throw it to the user */
1421 	l = message_from(target, LEVEL_NOTICE);
1422 
1423 	if (do_hook(GENERAL_NOTICE_LIST, "%s %s %s", from,real_target, message))
1424 	{
1425 	    if (hook_type == NOTICE_LIST)
1426 	    {
1427 		if (do_hook(hook_type, "%s %s", from, message))
1428 			put_it("-%s- %s", from, message);
1429 	    }
1430 	    else
1431 	    {
1432 		if (do_hook(hook_type, "%s %s %s", from, real_target, message))
1433 			put_it("-%s:%s- %s", from, real_target, message);
1434 	    }
1435 	}
1436 
1437 	/* Clean up and go home. */
1438 	pop_message_from(l);
1439 	set_server_doing_notice(from_server, 0);
1440 
1441 	/* Alas, this is not protected by protocol enforcement. :( */
1442 	notify_mark(from_server, from, 1, 0);
1443 }
1444 
rfc1459_odd(const char * from,const char * comm,const char ** ArgList)1445 void	rfc1459_odd (const char *from, const char *comm, const char **ArgList)
1446 {
1447 	const char *	stuff;
1448 
1449 	PasteArgs(ArgList, 0);
1450 	if (!(stuff = ArgList[0]))
1451 		stuff = empty_string;
1452 
1453 	if (!from || !*from)
1454 		from = "*";
1455 	if (!comm || !*comm)
1456 		comm = "*";
1457 
1458 	if (do_hook(ODD_SERVER_STUFF_LIST, "%s %s %s", from, comm, stuff))
1459 		say("Odd server stuff: \"%s %s\" (%s)", comm, stuff, from);
1460 }
1461 
1462 typedef struct {
1463         const char      *command;
1464         void            (*inbound_handler) (const char *, const char *, const char **);
1465         int             flags;
1466 } protocol_command;
1467 #define PROTO_QUOTEBAD  (1 << 0)
1468 
1469 static protocol_command rfc1459[] = {
1470 {	"ADMIN",	NULL,		0		},
1471 {	"AWAY",		NULL,		0		},
1472 { 	"CONNECT",	NULL,		0		},
1473 {	"ERROR",	p_error,	0		},
1474 {	"ERROR:",	p_error,	0		},
1475 {	"INFO",		NULL,		0		},
1476 {	"INVITE",	p_invite,	0		},
1477 {	"ISON",		NULL,		PROTO_QUOTEBAD	},
1478 {	"JOIN",		p_channel,	0		},
1479 {	"KICK",		p_kick,		0		},
1480 {	"KILL",		p_kill,		0		},
1481 {	"LINKS",	NULL,		0		},
1482 {	"LIST",		NULL,		0		},
1483 {	"MODE",		p_mode,		0		},
1484 {	"NAMES",	NULL,		0		},
1485 {	"NICK",		p_nick,		PROTO_QUOTEBAD	},
1486 {	"NOTICE",	p_notice,	0		},
1487 {	"OPER",		NULL,		0		},
1488 {	"PART",		p_part,		0		},
1489 {	"PASS",		NULL,		0 		},
1490 {	"PING",		p_ping,		0		},
1491 {	"PONG",		p_pong,		0		},
1492 {	"PRIVMSG",	p_privmsg,	0		},
1493 {	"QUIT",		p_quit,		PROTO_QUOTEBAD	},
1494 {	"REHASH",	NULL,		0		},
1495 {	"RESTART",	NULL,		0		},
1496 {	"RPONG",	p_rpong,	0		},
1497 {	"SERVER",	NULL,		PROTO_QUOTEBAD	},
1498 {	"SILENCE",	p_silence,	0		},
1499 {	"SQUIT",	NULL,		0		},
1500 {	"STATS",	NULL,		0		},
1501 {	"SUMMON",	NULL,		0		},
1502 {	"TIME",		NULL,		0		},
1503 {	"TOPIC",	p_topic,	0		},
1504 {	"TRACE",	NULL,		0		},
1505 {	"USER",		NULL,		0		},
1506 {	"USERHOST",	NULL,		PROTO_QUOTEBAD	},
1507 {	"USERS",	NULL,		0		},
1508 {	"VERSION",	NULL,		0		},
1509 {	"WALLOPS",	p_wallops,	0		},
1510 {	"WHO",		NULL,		PROTO_QUOTEBAD	},
1511 {	"WHOIS",	NULL,		0		},
1512 {	"WHOWAS",	NULL,		0		},
1513 {	NULL,		NULL,		0		}
1514 };
1515 #define NUMBER_OF_COMMANDS (sizeof(rfc1459) / sizeof(protocol_command)) - 2;
1516 static int 	num_protocol_cmds = -1;
1517 
1518 #define islegal(c) ((((c) >= 'A') && ((c) <= '~')) || \
1519                     (((c) >= '0') && ((c) <= '9')) || \
1520 		     ((c) == '*') || \
1521 		     ((c) & 0x80))
1522 
1523 /*
1524  * parse_server: parses messages from the server, doing what should be done
1525  * with them
1526  */
parse_server(const char * orig_line,size_t orig_line_size)1527 void 	parse_server (const char *orig_line, size_t orig_line_size)
1528 {
1529 	const char	*from;
1530 	const char	*comm;
1531 	const char	**ArgList;
1532 	const char	*TrueArgs[MAXPARA + 2];	/* Include space for command */
1533 	const char 	*OldFromUserHost;
1534 	int	loc;
1535 	char	*line;
1536 
1537 	if (num_protocol_cmds == -1)
1538 		num_protocol_cmds = NUMBER_OF_COMMANDS;
1539 
1540 	if (!orig_line || !*orig_line)
1541 		return;		/* empty line from server -- bye bye */
1542 
1543 	if (*orig_line == ':')
1544 	{
1545 		if (!do_hook(RAW_IRC_LIST, "%s", orig_line + 1))
1546 			return;
1547 	}
1548 	else if (!do_hook(RAW_IRC_LIST, "* %s", orig_line))
1549 		return;
1550 
1551 	if (inbound_line_mangler)
1552 	{
1553 	    char *s;
1554 	    s = new_normalize_string(orig_line, 1, inbound_line_mangler);
1555 	    line = LOCAL_COPY(s);
1556 	    new_free(&s);
1557 	}
1558 	else
1559 	    line = LOCAL_COPY(orig_line);
1560 
1561 	OldFromUserHost = FromUserHost;
1562 	FromUserHost = empty_string;
1563 	ArgList = TrueArgs;
1564 	BreakArgs(line, &from, ArgList);
1565 
1566 	if ((!(comm = *ArgList++)) || !from || !*ArgList)
1567 	{
1568 		rfc1459_odd(from, comm, ArgList);
1569 		return;		/* Serious protocol violation -- ByeBye */
1570 	}
1571 
1572 	if (*from && !islegal(*from))
1573 	{
1574 		rfc1459_odd(from, comm, ArgList);
1575 		return;
1576 	}
1577 
1578 	/* Some day all this needs to be replaced with an alist. */
1579 	if (is_number(comm))
1580 		numbered_command(from, comm, ArgList);
1581 	else
1582 	{
1583 		for (loc = 0; rfc1459[loc].command; loc++)
1584 			if (!strcmp(rfc1459[loc].command, comm))
1585 				break;
1586 
1587 		if (rfc1459[loc].command && rfc1459[loc].inbound_handler)
1588 			rfc1459[loc].inbound_handler(from, comm, ArgList);
1589 		else
1590 			rfc1459_odd(from, comm, ArgList);
1591 	}
1592 
1593 	FromUserHost = OldFromUserHost;
1594 	from_server = -1;
1595 }
1596 
1597 /*
1598  * rfc1459_any_to_utf8	- Pre-process an RFC1459 message so it's in utf8.
1599  *
1600  * Arguments:
1601  *	buffer - A null terminated RFC1459 message (not in utf8 already)
1602  *		 Upon return, if possible, will hold the message in utf8.
1603  *		 If not possible, 'extra' will hold the message.
1604  *	buffsiz - How many bytes 'buffer' can hold.
1605  *	extra - A pointer to NULL -- if 'buffer' is bigger than 'buffsiz'
1606  *		after converting to utf8, then this will be set to a
1607  *		new_malloc()ed string.  YOU MUST new_free() THIS IF IT IS SET!
1608  *
1609  * This function divides an RFC1459 message into two parts
1610  *	1. The "server part"
1611  *	2. The "payload part"
1612  * The "server part" is formatted by the server, and contains nicks and
1613  * channel names, and those will be encoded with the server's encoding.
1614  * The "payload part" is encoded with whatever the sender of the message
1615  * is using, which may not be the same thing as the server.
1616  *
1617  * The "server part" is recoded first -- which yields utf8 channels and nicks,
1618  * which are then used to recode the "payload part".  The two parts are put
1619  * back together into "buffer" unless the result is bigger than "buffsiz"
1620  * in which case it's put into a new_malloc()ed buffer stashed in 'extra'.
1621  *
1622  * XXX Ugh.  I hate that I made this so complicated just to generalize this.
1623  */
rfc1459_any_to_utf8(char * buffer,size_t buffsiz,char ** extra)1624 void	rfc1459_any_to_utf8 (char *buffer, size_t buffsiz, char **extra)
1625 {
1626 	char *	server_part;
1627 	char *	payload_part;
1628 	size_t	bytes_needed = 0;
1629 	char *	from = NULL, *to = NULL;
1630 	char *	command = NULL;
1631 	char *  endp;
1632 	char *	extra_server_part = NULL;
1633 	char *	extra_payload_part = NULL;
1634 	int	bytes;
1635 
1636 	if (x_debug & DEBUG_RECODE)
1637 		yell(">> Received %s", buffer);
1638 
1639 	/*
1640 	 * For the benefit of Fusion, I want to support ISO-2022-JP.
1641 	 * This encoding is "valid" UTF-8, but is not UTF-8.  So we need
1642 	 * to check it first.
1643 	 *
1644 	 * Please see http://www.sljfaq.org/afaq/encodings.html
1645 	 * There are other Japanese encodings that collide with UTF-8,
1646 	 * but I don't have any users to test, so I'm focused on what
1647 	 * I can actually test.
1648 	 */
1649 	if (is_iso2022_jp(buffer))
1650 	{
1651 		if (x_debug & DEBUG_RECODE)
1652 			yell(">> This looks like a JIS message...");
1653 	}
1654 	/* If the data is already utf8, then do nothing. */
1655 	else if ((bytes = invalid_utf8str(buffer)) == 0)
1656 	{
1657 		return;
1658 	}
1659 	else
1660 	{
1661 		if (x_debug & DEBUG_RECODE)
1662 			yell(">> There are %d invalid utf8 sequences...", bytes);
1663 	}
1664 
1665 	/*
1666 	 * Point the "server part" at the start, and move the
1667 	 * "payload part" to the argument starting with colon.
1668 	 */
1669 	server_part = buffer;
1670 	for (payload_part = server_part; *payload_part; payload_part++)
1671 	{
1672 		if (payload_part[0] == ' ' && payload_part[1] == ':')
1673 		{
1674 			*payload_part++ = 0;	/* Whack the space */
1675 			if (x_debug & DEBUG_RECODE)
1676 				yell(">> Found payload (%ld bytes): %s",
1677 					(long)strlen(payload_part), payload_part);
1678 			break;
1679 		}
1680 	}
1681 
1682 	if (x_debug & DEBUG_RECODE)
1683 	{
1684 		yell(">> server part is %s, payload_part is %s",
1685 			server_part, payload_part);
1686 	}
1687 
1688 	/*
1689 	 * If "payload_part" is pointing at a nul here, there was no payload.
1690 	 */
1691 
1692 
1693 	/*
1694 	 * If the server part is not in utf8, then we need to convert it to
1695 	 * utf8 using the server's encoding to get nicks and channels in utf8.
1696 	 */
1697 	if (is_iso2022_jp(server_part) || invalid_utf8str(server_part))
1698 	{
1699 		if (x_debug & DEBUG_RECODE)
1700 			say(">> Need to recode server part for %d", from_server);
1701 
1702 		/*
1703 		 * XXX The use of the bogus nick "zero" is just because
1704 		 * ``from'' can't be NULL, but we want it to use the
1705 		 * server's default encoding.
1706 		 */
1707 		inbound_recode(zero, from_server, NULL, server_part, &extra_server_part);
1708 		if (extra_server_part)
1709 			server_part = extra_server_part;
1710 
1711 		if (x_debug & DEBUG_RECODE)
1712 			say(">> Recoded server part: %s", server_part);
1713 	}
1714 
1715 	/*
1716 	 * If the payload part exists and is not valid utf8, then we need
1717 	 * to figure out who sent this message, and recode it with their
1718 	 * encoding.  This deals with channels and nicks already in utf8.
1719 	 */
1720 	if (*payload_part &&
1721 	     (is_iso2022_jp(payload_part) || invalid_utf8str(payload_part)))
1722 	do
1723 	{
1724 		char *	server_part_copy;
1725 
1726 		if (x_debug & DEBUG_RECODE)
1727 			say(">> Need to recode payload part for %d", from_server);
1728 
1729 		server_part_copy = LOCAL_COPY(server_part);
1730 
1731 		/*
1732 		 * Figure out who the sender is (-> "from")
1733 		 * Put 'endp' at the start of the command word
1734 		 */
1735 		if (*server_part_copy == ':')
1736 		{
1737 			from = server_part_copy + 1;
1738 			for (endp = from; *endp; endp++)
1739 			{
1740 				if (*endp == '!')
1741 					*endp++ = 0;
1742 
1743 				/* Not connected, so don't "fix" it */
1744 				if (*endp == ' ')
1745 				{
1746 					*endp++ = 0;
1747 					break;
1748 				}
1749 			}
1750 
1751 			/*
1752 			 * So now 'from' points to the nick or server
1753 			 * that sent us the message, and 'endp' points
1754 			 * at the word after the prefix
1755 			 */
1756 		}
1757 		else
1758 		{
1759 			from = NULL;
1760 			endp = server_part_copy;
1761 		}
1762 
1763 		/* Skip over the command word */
1764 		command = endp;
1765 		for (; *endp; endp++)
1766 		{
1767 			if (*endp == ' ')
1768 			{
1769 				*endp++ = 0;
1770 				break;
1771 			}
1772 		}
1773 
1774 		/* "endp" points at the target word (or a nul) */
1775 		to = endp;
1776 		for (; *endp; endp++)
1777 		{
1778 			if (*endp == ' ')
1779 			{
1780 				*endp++ = 0;
1781 				break;
1782 			}
1783 		}
1784 
1785 		if (from && !*from)
1786 			from = NULL;
1787 		if (to && !*to)
1788 			to = NULL;
1789 
1790 		/*
1791 		 * XXX UGH! BLEH! HIDEOUS!
1792 		 *
1793 		 * Some things are not recode ready.  We must detect them and
1794 		 * then force them to recode on their own later.
1795 		 * This is where the special cases are handled.
1796 		 */
1797 
1798 		/*
1799 		 * Special case #1 -- CTCP messages
1800 		 * Description:
1801 		 *    A PRIVMSG or NOTICE where the first byte of the
1802 		 * 	payload is \001 and the final bytes of the payload
1803 		 *	are \001\r\n, and there are no intervening \001s
1804 		 * 	shall be treated as a well-formed CTCP message/request
1805 		 *	and is not subject to recoding.
1806 		 */
1807 		if (!strcmp(command, "PRIVMSG") || !strcmp(command, "NOTICE"))
1808 		{
1809 			if (payload_part[0] == ':' && payload_part[1] == '\001')
1810 			{
1811 				const char *p;
1812 
1813 				/* The second \001 must be before newline */
1814 				p = strchr(payload_part + 2, '\001');
1815 				if (p && p[1] == 0)
1816 					break;
1817 
1818 				/* Otherwise it's nonsense; recode it */
1819 			}
1820 		}
1821 
1822 
1823 		/*
1824 		 * Everything else isn't subject to special casing.
1825 		 */
1826 		if (x_debug & DEBUG_RECODE)
1827 			say(">> Recoding payload from [%s], to [%s], server [%d]",
1828 				from?from:"", to?to:"", from_server);
1829 
1830 		/* UTF8-ify the payload with 'from' and 'to' */
1831 		inbound_recode(from, from_server, to, payload_part, &extra_payload_part);
1832 		if (extra_payload_part)
1833 			payload_part = extra_payload_part;
1834 
1835 		if (x_debug & DEBUG_RECODE)
1836 			say(">> Recoded payload part: %s", payload_part);
1837 	}
1838 	while (0);
1839 
1840 	/* Make copies just to get them out of 'buffer' */
1841 	server_part = LOCAL_COPY(server_part);
1842 	payload_part = LOCAL_COPY(payload_part);
1843 
1844 	if (x_debug & DEBUG_RECODE)
1845 		say(">> server part: %s", server_part);
1846 	if (x_debug & DEBUG_RECODE)
1847 		say(">> payload part: %s", payload_part);
1848 
1849 	/*
1850 	 * Now paste the two parts back together.
1851 	 */
1852 	bytes_needed = strlen(server_part);
1853 	if (*payload_part)
1854 		bytes_needed += strlen(payload_part) + 1;
1855 
1856 	if (bytes_needed > buffsiz)
1857 	{
1858 		*extra = new_malloc(bytes_needed + 2);
1859 		buffer = *extra;
1860 		buffsiz = bytes_needed + 1;
1861 	}
1862 
1863 	strlcpy(buffer, server_part, buffsiz);
1864 	if (*payload_part)
1865 	{
1866 		strlcat(buffer, " ", buffsiz);
1867 		strlcat(buffer, payload_part, buffsiz);
1868 	}
1869 
1870 	if (x_debug & DEBUG_RECODE)
1871 		say(">> Reconstituted UTF8 message: %s", buffer);
1872 
1873 	new_free(&extra_server_part);
1874 	new_free(&extra_payload_part);
1875 }
1876 
1877