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