1 /*
2  * funny.c: Well, I put some stuff here and called it funny.  So sue me.
3  *
4  * Written by Michael Sandrof
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2021 Matthew R. Green.
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  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: funny.c,v 1.58 2021/02/24 10:38:03 mrg Exp $");
37 
38 #include "ircaux.h"
39 #include "hook.h"
40 #include "vars.h"
41 #include "funny.h"
42 #include "names.h"
43 #include "server.h"
44 #include "lastlog.h"
45 #include "ircterm.h"
46 #include "output.h"
47 #include "numbers.h"
48 #include "parse.h"
49 #include "screen.h"
50 #include "sl_irc.h"
51 
52 static	u_char	*match_str = NULL;
53 
54 static	int	funny_min;
55 static	int	funny_max;
56 static	int	funny_flags;
57 
58 void
funny_match(u_char * stuff)59 funny_match(u_char *stuff)
60 {
61 	malloc_strcpy(&match_str, stuff);
62 }
63 
64 void
set_funny_flags(int min,int max,int flags)65 set_funny_flags(int min, int max, int flags)
66 {
67 	funny_min = min;
68 	funny_max = max;
69 	funny_flags = flags;
70 }
71 
72 struct	WideListInfoStru
73 {
74 	u_char	*channel;
75 	int	users;
76 };
77 
78 typedef	struct WideListInfoStru WideList;
79 
80 static	WideList **wide_list = NULL;
81 static	int	wl_size = 0;
82 static	size_t	wl_elements = 0;
83 
84 static	int	funny_widelist_users(WideList **, WideList **);
85 static	int	funny_widelist_names(WideList **, WideList **);
86 
87 static	int
funny_widelist_users(WideList ** left,WideList ** right)88 funny_widelist_users(WideList **left, WideList **right)
89 {
90 	if ((**left).users > (**right).users)
91 		return -1;
92 	else if ((**right).users > (**left).users)
93 		return 1;
94 	else
95 		return my_stricmp((**left).channel, (**right).channel);
96 }
97 
98 static	int
funny_widelist_names(WideList ** left,WideList ** right)99 funny_widelist_names(WideList **left, WideList **right)
100 {
101 	int	comp;
102 
103 	if ((comp = my_stricmp((**left).channel, (**right).channel)) != 0)
104 		return comp;
105 	else if ((**left).users > (**right).users)
106 		return -1;
107 	else if ((**right).users > (**left).users)
108 		return 1;
109 	else
110 		return 0;
111 }
112 
113 
114 void
funny_print_widelist(void)115 funny_print_widelist(void)
116 {
117 	int	i;
118 	StringList	*sl;
119 	size_t	tlen;
120 
121 	if (!wide_list)
122 		return;
123 
124 	if (funny_flags & FUNNY_NAME)
125 		qsort((void *) wide_list, wl_elements, sizeof(WideList *),
126 			(int (*)(const void *, const void *)) funny_widelist_names);
127 	else if (funny_flags & FUNNY_USERS)
128 		qsort((void *) wide_list, wl_elements, sizeof(WideList *),
129 			(int (*)(const void *, const void *)) funny_widelist_users);
130 
131 	sl = sl_init();
132 	tlen = 0;
133 	for (i = 0; i < wl_elements; i++)
134 	{
135 		u_char	*entry = NULL;
136 		size_t	elen;
137 
138 		malloc_snprintf(&entry, "%s(%d) ", wide_list[i]->channel,
139 				wide_list[i]->users);
140 		elen = my_strlen(entry);
141 		if (tlen + elen > get_co() - 5)
142 		{
143 			u_char	*buffer = sl_concat(sl, empty_string());
144 			if (do_hook(WIDELIST_LIST, "%s", buffer))
145 				say("%s", buffer);
146 			new_free(&buffer);
147 			sl_free(sl, 1);
148 			sl = sl_init();
149 			tlen = 0;
150 		}
151 		sl_add(sl, CP(entry));
152 		tlen += elen;
153 	}
154 	if (sl_size(sl))
155 	{
156 		u_char	*buffer = sl_concat(sl, empty_string());
157 		if (do_hook(WIDELIST_LIST, "%s", buffer))
158 			say("%s" , buffer);
159 		new_free(&buffer);
160 	}
161 	sl_free(sl, 1);
162 	for (i = 0; i < wl_elements; i++)
163 	{
164 		new_free(&wide_list[i]->channel);
165 		new_free(&wide_list[i]);
166 	}
167 	new_free(&wide_list);
168 	wl_elements = wl_size = 0;
169 }
170 
171 void
funny_list(u_char * comm,u_char * from,u_char ** args)172 funny_list(u_char *comm, u_char *from, u_char **args)
173 {
174 	u_char	*channel,
175 		*user_cnt,
176 		*line;
177 	WideList **new_list;
178 	int	cnt;
179 	static	u_char	format[25];
180 	static	int	last_width = -1;
181 
182 	if (check_params(comm, from, 0, args, 3))
183 		return;
184 
185 	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
186 	{
187 		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
188 			snprintf(CP(format), sizeof format, "*** %%-%u.%us %%-5s  %%s",
189 				(u_char) last_width,
190 				(u_char) last_width);
191 		else
192 			my_strmcpy(format, "*** %s\t%-5s  %s", sizeof format);
193 	}
194 	channel = args[0];
195 	user_cnt = args[1];
196 	line = paste_args(args, 2);
197 	if (funny_flags & FUNNY_TOPIC && !(line && *line))
198 		return;
199 	cnt = my_atoi(user_cnt);
200 	if (funny_min && (cnt < funny_min))
201 		return;
202 	if (funny_max && (cnt > funny_max))
203 		return;
204 	if ((funny_flags & FUNNY_PRIVATE) && (*channel != '*'))
205 		return;
206 	if ((funny_flags & FUNNY_PUBLIC) && (*channel == '*'))
207 		return;
208 	if (match_str)
209 	{
210 		if (wild_match(match_str, channel) == 0)
211 			return;
212 	}
213 	if (funny_flags & FUNNY_WIDE)
214 	{
215 		if (wl_elements >= wl_size)
216 		{
217 			const size_t increase = 50;
218 			new_list = new_malloc(sizeof(*new_list) * (wl_size + increase));
219 			memset(new_list, 0, sizeof(*new_list) * (wl_size + increase));
220 			if (wl_size)
221 				memmove(new_list, wide_list, sizeof(*new_list) * wl_size);
222 			wl_size += increase;
223 			new_free(&wide_list);
224 			wide_list = new_list;
225 		}
226 		wide_list[wl_elements] = new_malloc(sizeof **wide_list);
227 		wide_list[wl_elements]->users = cnt;
228 		wide_list[wl_elements]->channel = NULL;
229 		malloc_strcpy(&(wide_list[wl_elements]->channel),
230 				(*channel != '*') ? channel : (u_char *) "Prv");
231 		wl_elements++;
232 		return;
233 	}
234 	if (do_hook(current_numeric(), "%s %s %s %s",
235 		    from, channel, user_cnt, line) &&
236 	    do_hook(LIST_LIST, "%s %s %s", channel, user_cnt, line))
237 	{
238 		if (channel && user_cnt)
239 		{
240 			if (*channel == '*')
241 				put_it(CP(format), "Prv", user_cnt, line);
242 			else
243 				put_it(CP(format), channel, user_cnt, line);
244 		}
245 	}
246 }
247 
248 void
funny_namreply(u_char * comm,u_char * from,u_char ** args)249 funny_namreply(u_char *comm, u_char *from, u_char **args)
250 {
251 	u_char	*type,
252 		*nick,
253 		*channel;
254 	static	u_char	format[40];
255 	static	int	last_width = -1;
256 	int	cnt;
257 	u_char	*ptr;
258 	u_char	*line;
259 
260 	if (check_params(comm, from, 0, args, 3))
261 		return;
262 
263 	paste_args(args, 2);
264 	type = args[0];
265 	channel = args[1];
266 	line = args[2];
267 	save_message_from();
268 	message_from(channel, LOG_CRAP);
269 	if (channel_mode_lookup(channel, CHAN_NAMES | CHAN_MODE, CHAN_NAMES))
270 	{
271 		if (do_hook(current_numeric(), "%s %s %s %s", from, type, channel,
272 			line) && get_int_var(SHOW_CHANNEL_NAMES_VAR))
273 			say("Users on %s: %s", channel, line);
274 		while ((nick = next_arg(line, &line)) != NULL)
275 			add_to_channel(channel, nick, parsing_server(), 0, 0);
276 		goto out;
277 	}
278 	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
279 	{
280 		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
281 			snprintf(CP(format), sizeof format, "%%s: %%-%u.%us %%s",
282 				(u_char) last_width,
283 				(u_char) last_width);
284 		else
285 			my_strmcpy(format, "%s: %s\t%s", sizeof format);
286 	}
287 	ptr = line;
288 	for (cnt = -1; ptr; cnt++)
289 	{
290 		if ((ptr = my_index(ptr, ' ')) != NULL)
291 			ptr++;
292 	}
293 	if (funny_min && (cnt < funny_min))
294 		return;
295 	else if (funny_max && (cnt > funny_max))
296 		return;
297 	if ((funny_flags & FUNNY_PRIVATE) && (*type == '='))
298 		return;
299 	if ((funny_flags & FUNNY_PUBLIC) && (*type == '*'))
300 		return;
301 	if (type && channel)
302 	{
303 		if (match_str)
304 		{
305 			if (wild_match(match_str, channel) == 0)
306 				return;
307 		}
308 		if (do_hook(current_numeric(), "%s %s %s %s", from, type, channel,
309 			line) && do_hook(NAMES_LIST, "%s %s", channel, line))
310 		{
311 			switch (*type)
312 			{
313 			case '=':
314 				if (last_width && (my_strlen(channel) > last_width))
315 				{
316 					channel[last_width-1] = '>';
317 					channel[last_width] = '\0';
318 				}
319 				put_it(CP(format), "Pub", channel, line);
320 				break;
321 			case '*':
322 				put_it(CP(format), "Prv", channel, line);
323 				break;
324 			case '@':
325 				put_it(CP(format), "Sec", channel, line);
326 				break;
327 			}
328 		}
329 	}
330 out:
331 	restore_message_from();
332 }
333 
334 void
funny_mode(u_char * from,u_char ** args)335 funny_mode(u_char *from, u_char **args)
336 {
337 	u_char	*mode, *channel;
338 
339 	if (server_get_version(parsing_server()) < Server2_6)
340 	{
341 		paste_args(args, 0);
342 		channel = NULL;
343 		mode = args[0];
344 	}
345 	else
346 	{
347 		if (!args[1])
348 			return;
349 		paste_args(args, 1);
350 		channel = args[0];
351 		mode = args[1];
352 	}
353 	/* if (ignore_mode) */
354 	if (channel && channel_mode_lookup(channel, CHAN_NAMES | CHAN_MODE, CHAN_MODE))
355 	{
356 		update_channel_mode(channel, parsing_server(), mode);
357 		update_all_status();
358 	}
359 	else
360 	{
361 		save_message_from();
362 		message_from(channel, LOG_CRAP);
363 		if (channel)
364 		{
365 			if (do_hook(current_numeric(), "%s %s %s", from,
366 					channel, mode))
367 				put_it("%s Mode for channel %s is \"%s\"",
368 					numeric_banner(), channel, mode);
369 		}
370 		else
371 		{
372 			if (do_hook(current_numeric(), "%s %s", from, mode))
373 				put_it("%s Channel mode is \"%s\"",
374 					numeric_banner(), mode);
375 		}
376 		restore_message_from();
377 	}
378 }
379 
380 void
update_user_mode(u_char * modes)381 update_user_mode(u_char *modes)
382 {
383 	int	onoff = 1;
384 
385 	while (*modes)
386 	{
387 		switch(*modes++)
388 		{
389 		case '-':
390 			onoff = 0;
391 			break;
392 		case '+':
393 			onoff = 1;
394 			break;
395 		case 'o':
396 		case 'O':
397 			server_set_operator(parsing_server(), onoff);
398 			break;
399 		case 's':
400 		case 'S':
401 			server_set_flag(parsing_server(), USER_MODE_S, onoff);
402 			break;
403 		case 'i':
404 		case 'I':
405 			server_set_flag(parsing_server(), USER_MODE_I, onoff);
406 			break;
407 		case 'w':
408 		case 'W':
409 			server_set_flag(parsing_server(), USER_MODE_W, onoff);
410 			break;
411 		case 'r':
412 			server_set_flag(parsing_server(), USER_MODE_R, onoff);
413 			break;
414 		case 'a':
415 			server_set_flag(parsing_server(), USER_MODE_A, onoff);
416 			break;
417 		case 'z':
418 			server_set_flag(parsing_server(), USER_MODE_Z, onoff);
419 			break;
420 		}
421 	}
422 }
423 
424 void
reinstate_user_modes(void)425 reinstate_user_modes(void)
426 {
427 	u_char	modes[10];
428 	u_char	*c;
429 
430 	if (server_get_version(parsing_server()) < Server2_7)
431 		return;
432 	c = modes;
433 	if (server_get_flag(parsing_server(), USER_MODE_W))
434 		*c++ = 'w';
435 	if (server_get_flag(parsing_server(), USER_MODE_S))
436 		*c++ = 's';
437 	if (server_get_flag(parsing_server(), USER_MODE_I))
438 		*c++ = 'i';
439 	*c = '\0';
440 	if (c != modes)
441 		send_to_server("MODE %s +%s",
442 			server_get_nickname(parsing_server()),
443 		modes);
444 }
445