1 
2 /*
3  *   Unreal Internet Relay Chat Daemon, src/s_misc.c
4  *   Copyright (C) 1990 Jarkko Oikarinen and
5  *                      University of Oulu, Computing Center
6  *
7  *   See file AUTHORS in IRC package for additional names of
8  *   the programmers.
9  *
10  *   This program is free software; you can redistribute it and/or modify
11  *   it under the terms of the GNU General Public License as published by
12  *   the Free Software Foundation; either version 1, or (at your option)
13  *   any later version.
14  *
15  *   This program is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with this program; if not, write to the Free Software
22  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24 
25 #ifndef CLEAN_COMPILE
26 static char sccsid[] =
27     "@(#)s_misc.c	2.42 3/1/94 (C) 1988 University of Oulu, \
28 Computing Center and Jarkko Oikarinen";
29 #endif
30 
31 #ifndef _WIN32
32 #include <sys/time.h>
33 #endif
34 #include "struct.h"
35 #include "common.h"
36 #include "sys.h"
37 #include "numeric.h"
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #if !defined(ULTRIX) && !defined(SGI) && \
41     !defined(__convex__) && !defined(_WIN32)
42 # include <sys/param.h>
43 #endif
44 #if defined(PCS) || defined(AIX) || defined(SVR3)
45 # include <time.h>
46 #endif
47 #ifdef HPUX
48 #include <unistd.h>
49 #endif
50 #ifdef _WIN32
51 # include <io.h>
52 #endif
53 #include "h.h"
54 #include "proto.h"
55 #include "channel.h"
56 #include <string.h>
57 
58 #ifndef NO_FDLIST
59 extern fdlist serv_fdlist;
60 extern fdlist oper_fdlist;
61 extern float currentrate;
62 extern float currentrate2;
63 #endif
64 extern ircstats IRCstats;
65 extern char	*me_hash;
66 
67 static void exit_one_client(aClient *, aClient *, aClient *, char *, int);
68 extern void exit_one_client_in_split(aClient *, aClient *, aClient *,
69     char *);
70 
71 static char *months[] = {
72 	"January", "February", "March", "April",
73 	"May", "June", "July", "August",
74 	"September", "October", "November", "December"
75 };
76 
77 static char *weekdays[] = {
78 	"Sunday", "Monday", "Tuesday", "Wednesday",
79 	"Thursday", "Friday", "Saturday"
80 };
81 
82 typedef struct {
83 	int value;			/** Unique integer value of item */
84 	char character;		/** Unique character assigned to item */
85 	char *name;			/** Name of item */
86 } BanActTable;
87 
88 static BanActTable banacttable[] = {
89 	{ BAN_ACT_KILL,		'K',	"kill" },
90 	{ BAN_ACT_TEMPSHUN,	'S',	"tempshun" },
91 	{ BAN_ACT_SHUN,		's',	"shun" },
92 	{ BAN_ACT_KLINE,	'k',	"kline" },
93 	{ BAN_ACT_ZLINE,	'z',	"zline" },
94 	{ BAN_ACT_GLINE,	'g',	"gline" },
95 	{ BAN_ACT_GZLINE,	'Z',	"gzline" },
96 	{ BAN_ACT_BLOCK,	'b',	"block" },
97 	{ BAN_ACT_DCCBLOCK,	'd',	"dccblock" },
98 	{ BAN_ACT_VIRUSCHAN,'v',	"viruschan" },
99 	{ BAN_ACT_WARN,		'w',	"warn" },
100 	{ 0, 0, 0 }
101 };
102 
103 typedef struct {
104 	int value;			/** Unique integer value of item */
105 	char character;		/** Unique character assigned to item */
106 	char *name;			/** Name of item */
107 	char *irccommand;	/** Raw IRC command of item (not unique!) */
108 } SpamfilterTargetTable;
109 
110 SpamfilterTargetTable spamfiltertargettable[] = {
111 	{ SPAMF_CHANMSG,	'c',	"channel",			"PRIVMSG" },
112 	{ SPAMF_USERMSG,	'p',	"private",			"PRIVMSG" },
113 	{ SPAMF_USERNOTICE,	'n',	"private-notice",	"NOTICE" },
114 	{ SPAMF_CHANNOTICE,	'N',	"channel-notice",	"NOTICE" },
115 	{ SPAMF_PART,		'P',	"part",				"PART" },
116 	{ SPAMF_QUIT,		'q',	"quit",				"QUIT" },
117 	{ SPAMF_DCC,		'd',	"dcc",				"PRIVMSG" },
118 	{ SPAMF_USER,		'u',	"user",				"NICK" },
119 	{ SPAMF_AWAY,		'a',	"away",				"AWAY" },
120 	{ SPAMF_TOPIC,		't',	"topic",			"TOPIC" },
121 	{ 0, 0, 0 }
122 };
123 
124 
125 /*
126  * stats stuff
127  */
128 struct stats ircst, *ircstp = &ircst;
129 
date(time_t clock)130 char *date(time_t clock)
131 {
132 	static char buf[80], plus;
133 	struct tm *lt, *gm;
134 	struct tm gmbuf;
135 	int  minswest;
136 
137 	if (!clock)
138 		time(&clock);
139 	gm = gmtime(&clock);
140 	bcopy((char *)gm, (char *)&gmbuf, sizeof(gmbuf));
141 	gm = &gmbuf;
142 	lt = localtime(&clock);
143 #ifndef _WIN32
144 	if (lt->tm_yday == gm->tm_yday)
145 		minswest = (gm->tm_hour - lt->tm_hour) * 60 +
146 		    (gm->tm_min - lt->tm_min);
147 	else if (lt->tm_yday > gm->tm_yday)
148 		minswest = (gm->tm_hour - (lt->tm_hour + 24)) * 60;
149 	else
150 		minswest = ((gm->tm_hour + 24) - lt->tm_hour) * 60;
151 #else
152 	minswest = (_timezone / 60);
153 #endif
154 	plus = (minswest > 0) ? '-' : '+';
155 	if (minswest < 0)
156 		minswest = -minswest;
157 	(void)ircsprintf(buf, "%s %s %d %d -- %02d:%02d %c%02d:%02d",
158 	    weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
159 	    1900 + lt->tm_year,
160 	    lt->tm_hour, lt->tm_min, plus, minswest / 60, minswest % 60);
161 
162 	return buf;
163 }
164 
165 
convert_time(time_t ltime)166 char *convert_time (time_t ltime)
167 {
168 	unsigned long days = 0,hours = 0,minutes = 0,seconds = 0;
169 	static char buffer[40];
170 
171 
172 	*buffer = '\0';
173 	seconds = ltime % 60;
174 	ltime = (ltime - seconds) / 60;
175 	minutes = ltime%60;
176 	ltime = (ltime - minutes) / 60;
177 	hours = ltime % 24;
178 	days = (ltime - hours) / 24;
179 	ircsprintf(buffer, "%ludays %luhours %luminutes %lusecs",
180 days, hours, minutes, seconds);
181 	return(*buffer ? buffer : "");
182 }
183 
184 
185 /*
186  *  Fixes a string so that the first white space found becomes an end of
187  * string marker (`\-`).  returns the 'fixed' string or "*" if the string
188  * was NULL length or a NULL pointer.
189  */
check_string(char * s)190 char *check_string(char *s)
191 {
192 	static char star[2] = "*";
193 	char *str = s;
194 
195 	if (BadPtr(s))
196 		return star;
197 
198 	for (; *s; s++)
199 		if (isspace(*s))
200 		{
201 			*s = '\0';
202 			break;
203 		}
204 
205 	return (BadPtr(str)) ? star : str;
206 }
207 
make_user_host(char * name,char * host)208 char *make_user_host(char *name, char *host)
209 {
210 	static char namebuf[USERLEN + HOSTLEN + 6];
211 	char *s = namebuf;
212 
213 	bzero(namebuf, sizeof(namebuf));
214 	name = check_string(name);
215 	strncpyzt(s, name, USERLEN + 1);
216 	s += strlen(s);
217 	*s++ = '@';
218 	host = check_string(host);
219 	strncpyzt(s, host, HOSTLEN + 1);
220 	s += strlen(s);
221 	*s = '\0';
222 	return (namebuf);
223 }
224 
225 
226 /*
227  * create a string of form "foo!bar@fubar" given foo, bar and fubar
228  * as the parameters.  If NULL, they become "*".
229  */
make_nick_user_host_r(char * namebuf,char * nick,char * name,char * host)230 char *make_nick_user_host_r(char *namebuf, char *nick, char *name, char *host)
231 {
232 	char *s = namebuf;
233 
234 	bzero(namebuf, sizeof(namebuf));
235 	nick = check_string(nick);
236 	strncpyzt(namebuf, nick, NICKLEN + 1);
237 	s += strlen(s);
238 	*s++ = '!';
239 	name = check_string(name);
240 	strncpyzt(s, name, USERLEN + 1);
241 	s += strlen(s);
242 	*s++ = '@';
243 	host = check_string(host);
244 	strncpyzt(s, host, HOSTLEN + 1);
245 	s += strlen(s);
246 	*s = '\0';
247 	return (namebuf);
248 }
249 
250 /*
251  * create a string of form "foo!bar@fubar" given foo, bar and fubar
252  * as the parameters.  If NULL, they become "*".
253  */
make_nick_user_host(char * nick,char * name,char * host)254 char *make_nick_user_host(char *nick, char *name, char *host)
255 {
256 	static char namebuf[NICKLEN + USERLEN + HOSTLEN + 24];
257 
258 	return make_nick_user_host_r(namebuf, nick, name, host);
259 }
260 
261 
262 /**
263  ** myctime()
264  **   This is like standard ctime()-function, but it zaps away
265  **   the newline from the end of that string. Also, it takes
266  **   the time value as parameter, instead of pointer to it.
267  **   Note that it is necessary to copy the string to alternate
268  **   buffer (who knows how ctime() implements it, maybe it statically
269  **   has newline there and never 'refreshes' it -- zapping that
270  **   might break things in other places...)
271  **
272  **/
273 
myctime(time_t value)274 char *myctime(time_t value)
275 {
276 	static char buf[28];
277 	char *p;
278 
279 	(void)strlcpy(buf, ctime(&value), sizeof buf);
280 	if ((p = (char *)index(buf, '\n')) != NULL)
281 		*p = '\0';
282 
283 	return buf;
284 }
285 
286 /*
287 ** check_registered_user is used to cancel message, if the
288 ** originator is a server or not registered yet. In other
289 ** words, passing this test, *MUST* guarantee that the
290 ** sptr->user exists (not checked after this--let there
291 ** be coredumps to catch bugs... this is intentional --msa ;)
292 **
293 ** There is this nagging feeling... should this NOT_REGISTERED
294 ** error really be sent to remote users? This happening means
295 ** that remote servers have this user registered, althout this
296 ** one has it not... Not really users fault... Perhaps this
297 ** error message should be restricted to local clients and some
298 ** other thing generated for remotes...
299 */
check_registered_user(aClient * sptr)300 int  check_registered_user(aClient *sptr)
301 {
302 	if (!IsRegisteredUser(sptr))
303 	{
304 		sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "*");
305 		return -1;
306 	}
307 	return 0;
308 }
309 
310 /*
311 ** check_registered user cancels message, if 'x' is not
312 ** registered (e.g. we don't know yet whether a server
313 ** or user)
314 */
check_registered(aClient * sptr)315 int  check_registered(aClient *sptr)
316 {
317 	if (!IsRegistered(sptr))
318 	{
319 		sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "*");
320 		return -1;
321 	}
322 	return 0;
323 }
324 
325 /*
326 ** get_client_name
327 **      Return the name of the client for various tracking and
328 **      admin purposes. The main purpose of this function is to
329 **      return the "socket host" name of the client, if that
330 **	differs from the advertised name (other than case).
331 **	But, this can be used to any client structure.
332 **
333 **	Returns:
334 **	  "name[user@ip#.port]" if 'showip' is true;
335 **	  "name[sockethost]", if name and sockhost are different and
336 **	  showip is false; else
337 **	  "name".
338 **
339 ** NOTE 1:
340 **	Watch out the allocation of "nbuf", if either sptr->name
341 **	or sptr->sockhost gets changed into pointers instead of
342 **	directly allocated within the structure...
343 **
344 ** NOTE 2:
345 **	Function return either a pointer to the structure (sptr) or
346 **	to internal buffer (nbuf). *NEVER* use the returned pointer
347 **	to modify what it points!!!
348 */
get_client_name(aClient * sptr,int showip)349 char *get_client_name(aClient *sptr, int showip)
350 {
351 	static char nbuf[HOSTLEN * 2 + USERLEN + 5];
352 
353 	if (MyConnect(sptr))
354 	{
355 		if (showip)
356 			(void)ircsprintf(nbuf, "%s[%s@%s.%u]",
357 			    sptr->name,
358 			    (!(sptr->flags & FLAGS_GOTID)) ? "" :
359 			    sptr->username,
360 #ifdef INET6
361 			    inetntop(AF_INET6,
362 			    (char *)&sptr->ip, mydummy, MYDUMMY_SIZE),
363 #else
364 			    inetntoa((char *)&sptr->ip),
365 #endif
366 			    (unsigned int)sptr->port);
367 		else
368 		{
369 			if (mycmp(sptr->name, sptr->sockhost))
370 				(void)ircsprintf(nbuf, "%s[%s]",
371 				    sptr->name, sptr->sockhost);
372 			else
373 				return sptr->name;
374 		}
375 		return nbuf;
376 	}
377 	return sptr->name;
378 }
379 
get_client_host(aClient * cptr)380 char *get_client_host(aClient *cptr)
381 {
382 	static char nbuf[HOSTLEN * 2 + USERLEN + 5];
383 
384 	if (!MyConnect(cptr))
385 		return cptr->name;
386 	if (!cptr->hostp)
387 		return get_client_name(cptr, FALSE);
388 	(void)ircsprintf(nbuf, "%s[%-.*s@%-.*s]",
389 	    cptr->name, USERLEN,
390   	    (!(cptr->flags & FLAGS_GOTID)) ? "" : cptr->username,
391 	    HOSTLEN, cptr->hostp->h_name);
392 	return nbuf;
393 }
394 
395 /*
396  * Form sockhost such that if the host is of form user@host, only the host
397  * portion is copied.
398  */
get_sockhost(aClient * cptr,char * host)399 void get_sockhost(aClient *cptr, char *host)
400 {
401 	char *s;
402 	if ((s = (char *)index(host, '@')))
403 		s++;
404 	else
405 		s = host;
406 	strncpyzt(cptr->sockhost, s, sizeof(cptr->sockhost));
407 }
408 
remove_dcc_references(aClient * sptr)409 void remove_dcc_references(aClient *sptr)
410 {
411 aClient *acptr;
412 Link *lp, *nextlp;
413 Link **lpp, *tmp;
414 int found;
415 
416 	lp = sptr->user->dccallow;
417 	while(lp)
418 	{
419 		nextlp = lp->next;
420 		acptr = lp->value.cptr;
421 		for(found = 0, lpp = &(acptr->user->dccallow); *lpp; lpp=&((*lpp)->next))
422 		{
423 			if(lp->flags == (*lpp)->flags)
424 				continue; /* match only opposite types for sanity */
425 			if((*lpp)->value.cptr == sptr)
426 			{
427 				if((*lpp)->flags == DCC_LINK_ME)
428 				{
429 					sendto_one(acptr, ":%s %d %s :%s has been removed from "
430 						"your DCC allow list for signing off",
431 						me.name, RPL_DCCINFO, acptr->name, sptr->name);
432 				}
433 				tmp = *lpp;
434 				*lpp = tmp->next;
435 				free_link(tmp);
436 				found++;
437 				break;
438 			}
439 		}
440 
441 		if(!found)
442 			sendto_realops("[BUG] remove_dcc_references:  %s was in dccallowme "
443 				"list[%d] of %s but not in dccallowrem list!",
444 				acptr->name, lp->flags, sptr->name);
445 
446 		free_link(lp);
447 		lp = nextlp;
448 	}
449 }
450 
451 
452 /*
453 ** exit_client
454 **	This is old "m_bye". Name  changed, because this is not a
455 **	protocol function, but a general server utility function.
456 **
457 **	This function exits a client of *any* type (user, server, etc)
458 **	from this server. Also, this generates all necessary prototol
459 **	messages that this exit may cause.
460 **
461 **   1) If the client is a local client, then this implicitly
462 **	exits all other clients depending on this connection (e.g.
463 **	remote clients having 'from'-field that points to this.
464 **
465 **   2) If the client is a remote client, then only this is exited.
466 **
467 ** For convenience, this function returns a suitable value for
468 ** m_funtion return value:
469 **
470 **	FLUSH_BUFFER	if (cptr == sptr)
471 **	0		if (cptr != sptr)
472 */
exit_client(aClient * cptr,aClient * sptr,aClient * from,char * comment)473 int  exit_client(aClient *cptr, aClient *sptr, aClient *from, char *comment)
474 {
475 	aClient *acptr;
476 	aClient *next;
477 	time_t on_for;
478 	ConfigItem_listen *listen_conf;
479 	static char comment1[HOSTLEN + HOSTLEN + 2];
480 	static int recurse = 0;
481 
482 	if (MyConnect(sptr))
483 	{
484 #ifndef NO_FDLIST
485 #define FDLIST_DEBUG
486 #ifdef FDLIST_DEBUG
487 		{
488 			int i;
489 
490 			if (!IsAnOper(sptr))
491 			{
492 				for (i = oper_fdlist.last_entry; i; i--)
493 				{
494 					if (oper_fdlist.entry[i] == sptr->slot)
495 					{
496 						sendto_realops("[BUG] exit_client: oper_fdlist entry while not oper, fd=%d, user='%s'",
497 							sptr->slot, sptr->name);
498 						ircd_log(LOG_ERROR, "[BUG] exit_client: oper_fdlist entry while not oper, fd=%d, user='%s'",
499 							sptr->slot, sptr->name);
500 						delfrom_fdlist(sptr->slot, &oper_fdlist); /* be kind of enough to fix the problem.. */
501 						break; /* MUST break here */
502 					}
503 				}
504 			}
505 		}
506 #endif
507 		if (IsAnOper(sptr))
508 			delfrom_fdlist(sptr->slot, &oper_fdlist);
509 		if (IsServer(sptr))
510 			delfrom_fdlist(sptr->slot, &serv_fdlist);
511 #endif
512 		if (sptr->class)
513 		{
514 			sptr->class->clients--;
515 			if ((sptr->class->flag.temporary) && !sptr->class->clients && !sptr->class->xrefcount)
516 			{
517 				delete_classblock(sptr->class);
518 				sptr->class = NULL;
519 			}
520 		}
521 		if (IsClient(sptr))
522 			IRCstats.me_clients--;
523 		if (sptr->serv && sptr->serv->conf)
524 		{
525 			sptr->serv->conf->refcount--;
526 			Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d",
527 				sptr->name, sptr->serv->conf->servername, sptr->serv->conf->refcount));
528 			if (!sptr->serv->conf->refcount
529 			  && sptr->serv->conf->flag.temporary)
530 			{
531 				Debug((DEBUG_ERROR, "deleting temporary block %s", sptr->serv->conf->servername));
532 				delete_linkblock(sptr->serv->conf);
533 				sptr->serv->conf = NULL;
534 			}
535 		}
536 		if (IsServer(sptr))
537 		{
538 			IRCstats.me_servers--;
539 			ircd_log(LOG_SERVER, "SQUIT %s (%s)", sptr->name, comment);
540 		}
541 		free_pending_net(sptr);
542 		if (sptr->listener)
543 			if (sptr->listener->class && !IsOutgoing(sptr))
544 			{
545 				listen_conf = (ConfigItem_listen *) sptr->listener->class;
546 				listen_conf->clients--;
547 				if (listen_conf->flag.temporary
548 				    && (listen_conf->clients == 0))
549 				{
550 					/* Call listen cleanup */
551 					listen_cleanup();
552 				}
553 			}
554 		sptr->flags |= FLAGS_CLOSING;
555 		if (IsPerson(sptr))
556 		{
557 			RunHook2(HOOKTYPE_LOCAL_QUIT, sptr, comment);
558 			sendto_connectnotice(sptr->name, sptr->user, sptr, 1, comment);
559 			/* Clean out list and watch structures -Donwulff */
560 			hash_del_watch_list(sptr);
561 			if (sptr->user && sptr->user->lopt)
562 			{
563 				free_str_list(sptr->user->lopt->yeslist);
564 				free_str_list(sptr->user->lopt->nolist);
565 				MyFree(sptr->user->lopt);
566 			}
567 			on_for = TStime() - sptr->firsttime;
568 			if (IsHidden(sptr))
569 				ircd_log(LOG_CLIENT, "Disconnect - (%ld:%ld:%ld) %s!%s@%s [VHOST %s]",
570 					on_for / 3600, (on_for % 3600) / 60, on_for % 60,
571 					sptr->name, sptr->user->username,
572 					sptr->user->realhost, sptr->user->virthost);
573 			else
574 				ircd_log(LOG_CLIENT, "Disconnect - (%ld:%ld:%ld) %s!%s@%s",
575 					on_for / 3600, (on_for % 3600) / 60, on_for % 60,
576 					sptr->name, sptr->user->username, sptr->user->realhost);
577 		} else
578 		if (IsUnknown(sptr))
579 		{
580 			RunHook2(HOOKTYPE_UNKUSER_QUIT, sptr, comment);
581 		}
582 
583 		if (sptr->fd >= 0 && !IsConnecting(sptr))
584 		{
585 			if (cptr != NULL && sptr != cptr)
586 				sendto_one(sptr,
587 				    "ERROR :Closing Link: %s %s (%s)",
588 				    get_client_name(sptr, FALSE), cptr->name,
589 				    comment);
590 			else
591 				sendto_one(sptr, "ERROR :Closing Link: %s (%s)",
592 				    get_client_name(sptr, FALSE), comment);
593 		}
594 		/*
595 		   ** Currently only server connections can have
596 		   ** depending remote clients here, but it does no
597 		   ** harm to check for all local clients. In
598 		   ** future some other clients than servers might
599 		   ** have remotes too...
600 		   **
601 		   ** Close the Client connection first and mark it
602 		   ** so that no messages are attempted to send to it.
603 		   ** (The following *must* make MyConnect(sptr) == FALSE!).
604 		   ** It also makes sptr->from == NULL, thus it's unnecessary
605 		   ** to test whether "sptr != acptr" in the following loops.
606 		 */
607 		close_connection(sptr);
608 	}
609 
610 	/*
611 	 * Recurse down the client list and get rid of clients who are no
612 	 * longer connected to the network (from my point of view)
613 	 * Only do this expensive stuff if exited==server -Donwulff
614 	 */
615 
616 	if (IsServer(sptr))
617 	{
618 		/*
619 		 * Is this right? Not recreateing the split message if
620 		 * we have been called recursivly? I hope so, cuz thats
621 		 * the only way I could make this give the right servers
622 		 * in the quit msg. -Cabal95
623 		 */
624 		if (cptr && !recurse)
625 		{
626 			/*
627 			 * We are sure as we RELY on sptr->srvptr->name and
628 			 * sptr->name to be less or equal to HOSTLEN
629 			 * Waste of strlcpy/strlcat here
630 			*/
631 			(void)strcpy(comment1, sptr->srvptr->name);
632 			(void)strcat(comment1, " ");
633 			(void)strcat(comment1, sptr->name);
634 		}
635 		/*
636 		 * First, remove the clients on the server itself.
637 		 */
638 		for (acptr = client; acptr; acptr = next)
639 		{
640 			next = acptr->next;
641 			if (IsClient(acptr) && (acptr->srvptr == sptr))
642 				exit_one_client(NULL, acptr,
643 				    &me, comment1, 1);
644 		}
645 
646 		/*
647 		 * Now, go SQUIT off the servers which are down-stream of
648 		 * the one we just lost.
649 		 */
650 		recurse++;
651 		for (acptr = client; acptr; acptr = next)
652 		{
653 			next = acptr->next;
654 			if (IsServer(acptr) && acptr->srvptr == sptr)
655 				exit_client(sptr, acptr, sptr, comment1); /* RECURSION */
656 			/*
657 			 * I am not masking SQUITS like I do QUITs.  This
658 			 * is probobly something we could easily do, but
659 			 * how much savings is there really in something
660 			 * like that?
661 			 */
662 #ifdef DEBUGMODE
663 			else if (IsServer(acptr) &&
664 			    (find_server(acptr->serv->up, NULL) == sptr))
665 			{
666 				sendto_ops("WARNING, srvptr!=sptr but "
667 				    "find_server did!  Server %s on "
668 				    "%s thought it was on %s while "
669 				    "losing %s.  Tell coding team.",
670 				    acptr->name, acptr->serv->up,
671 				    acptr->srvptr ? acptr->
672 				    srvptr->name : "<noserver>", sptr->name);
673 				exit_client(sptr, acptr, sptr, comment1);
674 			}
675 #endif
676 		}
677 		recurse--;
678 		RunHook(HOOKTYPE_SERVER_QUIT, sptr);
679 	}
680 
681 
682 	/*
683 	 * Finally, clear out the server we lost itself
684 	 */
685 	exit_one_client(cptr, sptr, from, comment, recurse);
686 	return cptr == sptr ? FLUSH_BUFFER : 0;
687 }
688 
689 /*
690 ** Exit one client, local or remote. Assuming all dependants have
691 ** been already removed, and socket closed for local client.
692 */
693 /* DANGER: Ugly hack follows. */
694 /* Yeah :/ */
exit_one_client(aClient * cptr,aClient * sptr,aClient * from,char * comment,int split)695 static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, char *comment, int split)
696 {
697 	aClient *acptr;
698 	int  i;
699 	Link *lp;
700 	Membership *mp;
701 	/*
702 	   **  For a server or user quitting, propagage the information to
703 	   **  other servers (except to the one where is came from (cptr))
704 	 */
705 	if (IsMe(sptr))
706 	{
707 		sendto_ops("ERROR: tried to exit me! : %s", comment);
708 		return;		/* ...must *never* exit self!! */
709 	}
710 	else if (IsServer(sptr))
711 	{
712 		/*
713 		   ** Old sendto_serv_but_one() call removed because we now
714 		   ** need to send different names to different servers
715 		   ** (domain name matching)
716 		 */
717 		for (i = 0; i <= LastSlot; i++)
718 		{
719 
720 			if (!(acptr = local[i]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)
721 			    || (DontSendQuit(acptr) && split))
722 				continue;
723 			/*
724 			   ** SQUIT going "upstream". This is the remote
725 			   ** squit still hunting for the target. Use prefixed
726 			   ** form. "from" will be either the oper that issued
727 			   ** the squit or some server along the path that
728 			   ** didn't have this fix installed. --msa
729 			 */
730 			if (sptr->from == acptr)
731 			{
732 				sendto_one(acptr, ":%s SQUIT %s :%s", from->name, sptr->name, comment);
733 			}
734 			else
735 			{
736 				sendto_one(acptr, "SQUIT %s :%s", sptr->name, comment);
737 			}
738 		}
739 	}
740 	else if (!(IsPerson(sptr)))
741 		/* ...this test is *dubious*, would need
742 		   ** some thougth.. but for now it plugs a
743 		   ** nasty hole in the server... --msa
744 		 */
745 		;		/* Nothing */
746 	else if (sptr->name[0])	/* ...just clean all others with QUIT... */
747 	{
748 		/*
749 		   ** If this exit is generated from "m_kill", then there
750 		   ** is no sense in sending the QUIT--KILL's have been
751 		   ** sent instead.
752 		 */
753 		if ((sptr->flags & FLAGS_KILLED) == 0)
754 		{
755 			if (split == 0)
756 				sendto_serv_butone_token
757 				    (cptr, sptr->name, MSG_QUIT, TOK_QUIT,
758 				    ":%s", comment);
759 			else
760 				/*
761 				 * Then this is a split, only old (stupid)
762 				 * clients need to get quit messages
763 				 */
764 				sendto_serv_butone_quit(cptr, ":%s QUIT :%s",
765 				    sptr->name, comment);
766 		}
767 		/*
768 		   ** If a person is on a channel, send a QUIT notice
769 		   ** to every client (person) on the same channel (so
770 		   ** that the client can show the "**signoff" message).
771 		   ** (Note: The notice is to the local clients *only*)
772 		 */
773 		if (sptr->user)
774 		{
775 			sendto_common_channels(sptr, ":%s QUIT :%s",
776 			    sptr->name, comment);
777 
778 			if (!IsULine(sptr) && !split)
779 				if (sptr->user->server != me_hash)
780 					sendto_fconnectnotice(sptr->name, sptr->user, sptr, 1, comment);
781 			if (!MyClient(sptr))
782 			{
783 				RunHook2(HOOKTYPE_REMOTE_QUIT, sptr, comment);
784 			}
785 #ifdef JOINTHROTTLE
786 			cmodej_deluserentries(sptr);
787 #endif
788 			while ((mp = sptr->user->channel))
789 				remove_user_from_channel(sptr, mp->chptr);
790 
791 			/* Clean up invitefield */
792 			while ((lp = sptr->user->invited))
793 				del_invite(sptr, lp->value.chptr);
794 			/* again, this is all that is needed */
795 
796 			/* Clean up silencefield */
797 			while ((lp = sptr->user->silence))
798 				(void)del_silence(sptr, lp->value.cp);
799 
800 			/* Clean up dccallow list and (if needed) notify other clients
801 			 * that have this person on DCCALLOW that the user just left/got removed.
802 			 */
803 			remove_dcc_references(sptr);
804 
805 			/* For remote clients, we need to check for any outstanding async
806 			 * connects attached to this 'sptr', and set those records to NULL.
807 			 * Why not for local? Well, we already do that in close_connection ;)
808 			 */
809 			if (!MyConnect(sptr))
810 				unrealdns_delreq_bycptr(sptr);
811 		}
812 	}
813 
814 	/* Remove sptr from the client list */
815 	if (del_from_client_hash_table(sptr->name, sptr) != 1)
816 		Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x",
817 		    sptr, sptr->name,
818 		    sptr->from ? sptr->from->sockhost : "??host",
819 		    sptr->from, sptr->next, sptr->prev, sptr->fd,
820 		    sptr->status, sptr->user));
821 	if (IsRegisteredUser(sptr))
822 		hash_check_watch(sptr, RPL_LOGOFF);
823 	remove_client_from_list(sptr);
824 	return;
825 }
826 
827 
checklist(void)828 void checklist(void)
829 {
830 	aClient *acptr;
831 	int  i, j;
832 
833 	if (!(bootopt & BOOT_AUTODIE))
834 		return;
835 	for (j = i = 0; i <= LastSlot; i++)
836 		if (!(acptr = local[i]))
837 			continue;
838 		else if (IsClient(acptr))
839 			j++;
840 	if (!j)
841 	{
842 		exit(0);
843 	}
844 	return;
845 }
846 
initstats(void)847 void initstats(void)
848 {
849 	bzero((char *)&ircst, sizeof(ircst));
850 }
851 
verify_opercount(aClient * orig,char * tag)852 void verify_opercount(aClient *orig, char *tag)
853 {
854 int counted = 0;
855 aClient *acptr;
856 char text[2048];
857 
858 	for (acptr = client; acptr; acptr = acptr->next)
859 	{
860 		if (IsOper(acptr) && !IsHideOper(acptr))
861 			counted++;
862 	}
863 	if (counted == IRCstats.operators)
864 		return;
865 	sprintf(text, "[BUG] operator count bug! value in /lusers is '%d', we counted '%d', "
866 	               "user='%s', userserver='%s', tag=%s. Corrected. ",
867 	               IRCstats.operators, counted, orig->name ? orig->name : "<null>",
868 	               orig->srvptr ? orig->srvptr->name : "<null>", tag ? tag : "<null>");
869 #ifdef DEBUGMODE
870 	sendto_realops("%s", text);
871 #endif
872 	ircd_log(LOG_ERROR, "%s", text);
873 	IRCstats.operators = counted;
874 }
875 
876 /** Check if the specified hostname does not contain forbidden characters.
877  * RETURNS:
878  * 1 if ok, 0 if rejected.
879  */
valid_host(char * host)880 int valid_host(char *host)
881 {
882 char *p;
883 	for (p=host; *p; p++)
884 		if (!isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.') && (*p != ':'))
885 			return 0;
886 	return 1;
887 }
888 
889 /** Checks if the specified regex (or fast badwords) is valid.
890  * returns NULL in case of success [!],
891  * pointer to buffer with error message otherwise
892  * if check_broadness is 1, the function will attempt to determine
893  * if the given regex string is too broad (i.e. matches everything)
894  */
unreal_checkregex(char * s,int fastsupport,int check_broadness)895 char *unreal_checkregex(char *s, int fastsupport, int check_broadness)
896 {
897 int errorcode, errorbufsize, regex=0;
898 char *errtmp, *tmp;
899 static char errorbuf[512];
900 regex_t expr;
901 
902 	if (!fastsupport)
903 		goto Ilovegotos;
904 
905 	for (tmp = s; *tmp; tmp++) {
906 		if (!isalnum(*tmp) && !(*tmp >= 128)) {
907 			if ((s == tmp) && (*tmp == '*'))
908 				continue;
909 			if ((*(tmp + 1) == '\0') && (*tmp == '*'))
910 				continue;
911 			regex = 1;
912 			break;
913 		}
914 	}
915 	if (regex)
916 	{
917 Ilovegotos:
918 		errorcode = regcomp(&expr, s, REG_ICASE|REG_EXTENDED);
919 		if (errorcode > 0)
920 		{
921 			errorbufsize = regerror(errorcode, &expr, NULL, 0)+1;
922 			errtmp = MyMalloc(errorbufsize);
923 			regerror(errorcode, &expr, errtmp, errorbufsize);
924 			strncpyzt(errorbuf, errtmp, sizeof(errorbuf));
925 			free(errtmp);
926 			regfree(&expr);
927 			return errorbuf;
928 		}
929 		if (check_broadness && !regexec(&expr, "", 0, NULL, 0))
930 		{
931 			strncpyzt(errorbuf, "Regular expression is too broad", sizeof(errorbuf));
932 			regfree(&expr);
933 			return errorbuf;
934 		}
935 		regfree(&expr);
936 	}
937 	return NULL;
938 }
939 
940 
941 
942 #define SPF_REGEX_FLAGS (REG_ICASE|REG_EXTENDED|REG_NOSUB)
943 
944 /** Allocates a new Spamfilter entry and compiles/fills in the info.
945  * NOTE: originally I wanted to integrate both badwords and spamfilter
946  * into one function, but that was quickly getting ugly :(.
947  */
unreal_buildspamfilter(char * s)948 Spamfilter *unreal_buildspamfilter(char *s)
949 {
950 Spamfilter *e = MyMallocEx(sizeof(Spamfilter));
951 
952 	regcomp(&e->expr, s, SPF_REGEX_FLAGS);
953 	return e;
954 }
955 
956 
957 /*|| BAN ACTION ROUTINES FOLLOW ||*/
958 
959 
960 /** Converts a banaction string (eg: "kill") to an integer value (eg: BAN_ACT_KILL) */
banact_stringtoval(char * s)961 int banact_stringtoval(char *s)
962 {
963 BanActTable *b;
964 
965 	for (b = &banacttable[0]; b->value; b++)
966 		if (!strcasecmp(s, b->name))
967 			return b->value;
968 	return 0;
969 }
970 
971 /** Converts a banaction character (eg: 'K') to an integer value (eg: BAN_ACT_KILL) */
banact_chartoval(char c)972 int banact_chartoval(char c)
973 {
974 BanActTable *b;
975 
976 	for (b = &banacttable[0]; b->value; b++)
977 		if (b->character == c)
978 			return b->value;
979 	return 0;
980 }
981 
982 /** Converts a banaction value (eg: BAN_ACT_KILL) to a character value (eg: 'k') */
banact_valtochar(int val)983 char banact_valtochar(int val)
984 {
985 BanActTable *b;
986 
987 	for (b = &banacttable[0]; b->value; b++)
988 		if (b->value == val)
989 			return b->character;
990 	return '\0';
991 }
992 
993 /** Converts a banaction value (eg: BAN_ACT_KLINE) to a string (eg: "kline") */
banact_valtostring(int val)994 char *banact_valtostring(int val)
995 {
996 BanActTable *b;
997 
998 	for (b = &banacttable[0]; b->value; b++)
999 		if (b->value == val)
1000 			return b->name;
1001 	return "UNKNOWN";
1002 }
1003 
1004 /*|| BAN TARGET ROUTINES FOLLOW ||*/
1005 
1006 /** Extract target flags from string 's'. */
spamfilter_gettargets(char * s,aClient * sptr)1007 int spamfilter_gettargets(char *s, aClient *sptr)
1008 {
1009 SpamfilterTargetTable *e;
1010 int flags = 0;
1011 
1012 	for (; *s; s++)
1013 	{
1014 		for (e = &spamfiltertargettable[0]; e->value; e++)
1015 			if (e->character == *s)
1016 			{
1017 				flags |= e->value;
1018 				break;
1019 			}
1020 		if (!e->value && sptr)
1021 		{
1022 			sendnotice(sptr, "Unknown target type '%c'", *s);
1023 			return 0;
1024 		}
1025 	}
1026 	return flags;
1027 }
1028 
1029 /** Convert a string with a targetname to an integer value */
spamfilter_getconftargets(char * s)1030 int spamfilter_getconftargets(char *s)
1031 {
1032 SpamfilterTargetTable *e;
1033 
1034 	for (e = &spamfiltertargettable[0]; e->value; e++)
1035 		if (!strcmp(s, e->name))
1036 			return e->value;
1037 	return 0;
1038 }
1039 
1040 /** Create a string with (multiple) targets from an integer mask */
spamfilter_target_inttostring(int v)1041 char *spamfilter_target_inttostring(int v)
1042 {
1043 static char buf[128];
1044 SpamfilterTargetTable *e;
1045 char *p = buf;
1046 
1047 	for (e = &spamfiltertargettable[0]; e->value; e++)
1048 		if (v & e->value)
1049 			*p++ = e->character;
1050 	*p = '\0';
1051 	return buf;
1052 }
1053 
unreal_decodespace(char * s)1054 char *unreal_decodespace(char *s)
1055 {
1056 static char buf[512], *i, *o;
1057 	for (i = s, o = buf; (*i) && (o < buf+510); i++)
1058 		if (*i == '_')
1059 		{
1060 			if (i[1] != '_')
1061 				*o++ = ' ';
1062 			else {
1063 				*o++ = '_';
1064 				i++;
1065 			}
1066 		}
1067 		else
1068 			*o++ = *i;
1069 	*o = '\0';
1070 	return buf;
1071 }
1072 
unreal_encodespace(char * s)1073 char *unreal_encodespace(char *s)
1074 {
1075 static char buf[512], *i, *o;
1076 
1077 	if (!s)
1078 		return NULL; /* NULL in = NULL out */
1079 
1080 	for (i = s, o = buf; (*i) && (o < buf+509); i++)
1081 	{
1082 		if (*i == ' ')
1083 			*o++ = '_';
1084 		else if (*i == '_')
1085 		{
1086 			*o++ = '_';
1087 			*o++ = '_';
1088 		}
1089 		else
1090 			*o++ = *i;
1091 	}
1092 	*o = '\0';
1093 	return buf;
1094 }
1095 
1096 /** This is basically only used internally by dospamfilter()... */
cmdname_by_spamftarget(int target)1097 char *cmdname_by_spamftarget(int target)
1098 {
1099 SpamfilterTargetTable *e;
1100 
1101 	for (e = &spamfiltertargettable[0]; e->value; e++)
1102 		if (e->value == target)
1103 			return e->irccommand;
1104 	return "???";
1105 }
1106 
is_autojoin_chan(char * chname)1107 int is_autojoin_chan(char *chname)
1108 {
1109 char buf[512];
1110 char *p, *name;
1111 
1112 	if (OPER_AUTO_JOIN_CHANS)
1113 	{
1114 		strlcpy(buf, OPER_AUTO_JOIN_CHANS, sizeof(buf));
1115 
1116 		for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ","))
1117 			if (!strcasecmp(name, chname))
1118 				return 1;
1119 	}
1120 
1121 	if (AUTO_JOIN_CHANS)
1122 	{
1123 		strlcpy(buf, AUTO_JOIN_CHANS, sizeof(buf));
1124 
1125 		for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ","))
1126 			if (!strcasecmp(name, chname))
1127 				return 1;
1128 	}
1129 
1130 	return 0;
1131 }
1132 
getcloak(aClient * sptr)1133 char *getcloak(aClient *sptr)
1134 {
1135 	if (!*sptr->user->cloakedhost)
1136 	{
1137 		/* need to calculate (first-time) */
1138 		make_virthost(sptr, sptr->user->realhost, sptr->user->cloakedhost, 0);
1139 	}
1140 
1141 	return sptr->user->cloakedhost;
1142 }
1143 
1144 /** Kicks all insecure users on a +z channel */
kick_insecure_users(aChannel * chptr)1145 void kick_insecure_users(aChannel *chptr)
1146 {
1147 	Member *member, *mb2;
1148 	aClient *cptr;
1149 	char *comment = "Insecure user not allowed on secure channel (+z)";
1150 
1151 	for (member = chptr->members; member; member = mb2)
1152 	{
1153 		mb2 = member->next;
1154 		cptr = member->cptr;
1155 		if (MyClient(cptr) && !IsSecureConnect(cptr) && !IsULine(cptr))
1156 		{
1157 			RunHook5(HOOKTYPE_LOCAL_KICK, &me, &me, cptr, chptr, comment);
1158 
1159 			if ((chptr->mode.mode & MODE_AUDITORIUM) && is_chanownprotop(cptr, chptr))
1160 			{
1161 				sendto_chanops_butone(cptr, chptr, ":%s KICK %s %s :%s", me.name, chptr->chname, cptr->name, comment);
1162 				sendto_prefix_one(cptr, &me, ":%s KICK %s %s :%s", me.name, chptr->chname, cptr->name, comment);
1163 			}
1164 			else
1165 			{
1166 				sendto_channel_butserv(chptr, &me, ":%s KICK %s %s :%s", me.name, chptr->chname, cptr->name, comment);
1167 			}
1168 
1169 			sendto_serv_butone_token(&me, me.name, MSG_KICK, TOK_KICK, "%s %s :%s", chptr->chname, cptr->name, comment);
1170 
1171 			remove_user_from_channel(cptr, chptr);
1172 		}
1173 	}
1174 }
1175