Author: Kev <klmitch@undernet.org>
[ircu2.10.12-pk.git] / ircd / gline.c
1 /*
2  * IRC - Internet Relay Chat, ircd/gline.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Finland
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id$
21  */
22 #include "gline.h"
23 #include "client.h"
24 #include "ircd.h"
25 #include "ircd_alloc.h"
26 #include "ircd_reply.h"
27 #include "ircd_string.h"
28 #include "match.h"
29 #include "numeric.h"
30 #include "s_bsd.h"
31 #include "s_misc.h"
32 #include "send.h"
33 #include "struct.h"
34 #include "support.h"
35 #include "msg.h"
36 #include "numnicks.h"
37 #include "numeric.h"
38 #include "sys.h"    /* FALSE bleah */
39
40 #include <assert.h>
41 #include <string.h>
42
43 struct Gline* GlobalGlineList  = 0;
44 struct Gline* BadChanGlineList = 0;
45
46 static void
47 canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user)
48 {
49   char *tmp;
50
51   if (!(tmp = strchr(userhost, '@'))) {
52     *user_p = def_user;
53     *host_p = userhost;
54   } else {
55     *user_p = userhost;
56     *(tmp++) = '\0';
57     *host_p = tmp;
58   }
59 }
60
61 static struct Gline *
62 make_gline(char *userhost, char *reason, time_t expire, time_t lastmod,
63            unsigned int flags)
64 {
65   struct Gline *gline;
66   char *user, *host;
67
68   gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
69   assert(0 != gline);
70
71   DupString(gline->gl_reason, reason); /* initialize gline... */
72   gline->gl_expire = expire;
73   gline->gl_lastmod = lastmod;
74   gline->gl_flags = flags & GLINE_MASK;
75
76   if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
77     DupString(gline->gl_user, userhost); /* first, remember channel */
78     gline->gl_host = 0;
79
80     gline->gl_next = BadChanGlineList; /* then link it into list */
81     gline->gl_prev_p = &BadChanGlineList;
82     if (BadChanGlineList)
83       BadChanGlineList->gl_prev_p = &gline->gl_next;
84     BadChanGlineList = gline;
85   } else {
86     canon_userhost(userhost, &user, &host, "*"); /* find user and host */
87
88     DupString(gline->gl_user, user); /* remember them... */
89     DupString(gline->gl_host, host);
90
91     if (check_if_ipmask(host)) /* mark if it's an IP mask */
92       gline->gl_flags |= GLINE_IPMASK;
93
94     gline->gl_next = GlobalGlineList; /* then link it into list */
95     gline->gl_prev_p = &GlobalGlineList;
96     if (GlobalGlineList)
97       GlobalGlineList->gl_prev_p = &gline->gl_next;
98     GlobalGlineList = gline;
99   }
100
101   return gline;
102 }
103
104 static int
105 do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline)
106 {
107   struct Client *acptr;
108   int fd, retval = 0, tval;
109
110   if (!GlineIsActive(gline)) /* no action taken on inactive glines */
111     return 0;
112
113   for (fd = HighestFd; fd >= 0; --fd) {
114     /*
115      * get the users!
116      */
117     if ((acptr = LocalClientArray[fd])) {
118       if (!acptr->user)
119         continue;
120
121       if ((GlineIsIpMask(gline) ? match(gline->gl_host, acptr->sock_ip) :
122            match(gline->gl_host, acptr->sockhost)) == 0 &&
123           (!acptr->user->username ||
124            match(gline->gl_user, acptr->user->username) == 0)) {
125         /* ok, here's one that got G-lined */
126         sendto_one(acptr, ":%s %d %s :*** %s.", me.name, ERR_YOUREBANNEDCREEP,
127                    acptr->name, gline->gl_reason);
128
129         /* let the ops know about it */
130         sendto_op_mask(SNO_GLINE, "G-line active for %s",
131                        get_client_name(acptr, FALSE));
132
133         /* and get rid of him */
134         if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)",
135                                     gline->gl_reason)))
136           retval = tval; /* retain killed status */
137       }
138     }
139   }
140
141   return retval;
142 }
143
144 static void
145 propagate_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline)
146 {
147   if (GlineIsLocal(gline) || (IsUser(sptr) && !gline->gl_lastmod))
148     return;
149
150   if (gline->gl_lastmod)
151     sendcmdto_serv_butone(cptr, CMD_GLINE, sptr, "* %c%s%s%s %Tu %Tu :%s",
152                           GlineIsActive(gline) ? '+' : '-', gline->gl_user,
153                           GlineIsBadChan(gline) ? "" : "@",
154                           GlineIsBadChan(gline) ? "" : gline->gl_host,
155                           gline->gl_expire - TStime(), gline->gl_lastmod,
156                           gline->gl_reason);
157   else
158     sendcmdto_serv_butone(cptr, CMD_GLINE, sptr, "* %c%s%s%s %Tu :%s",
159                           GlineIsActive(gline) ? '+' : '-', gline->gl_user,
160                           GlineIsBadChan(gline) ? "" : "@",
161                           GlineIsBadChan(gline) ? "" : gline->gl_host,
162                           gline->gl_expire - TStime(), gline->gl_reason);
163 }
164
165 int 
166 gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
167           char *reason, time_t expire, time_t lastmod, unsigned int flags)
168 {
169   struct Gline *agline;
170
171   assert(0 != userhost);
172   assert(0 != reason);
173
174   /*
175    * You cannot set a negative (or zero) expire time, nor can you set an
176    * expiration time for greater than GLINE_MAX_EXPIRE.
177    */
178   if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) {
179     if (!IsServer(sptr) && MyConnect(sptr))
180       send_error_to_client(sptr, ERR_BADEXPIRE, expire);
181     return 0;
182   }
183
184   expire += TStime(); /* convert from lifetime to timestamp */
185
186   /* NO_OLD_GLINE allows *@#channel to work correctly */
187 #ifdef BADCHAN
188   if (*userhost == '#' || *userhost == '&' || *userhost == '+'
189 # ifndef NO_OLD_GLINE
190       || userhost[2] == '#' || userhost[2] == '&' || userhost[2] == '+'
191 # endif /* OLD_GLINE */
192       ) {
193 # ifndef LOCAL_BADCHAN
194     if (flags & GLINE_LOCAL)
195       return 0;
196 # endif
197     flags |= GLINE_BADCHAN;
198   }
199 #endif /* BADCHAN */
200
201   /* Inform ops... */
202   sendto_op_mask(SNO_GLINE, "%s adding %s %s for %s, expiring at "
203                  TIME_T_FMT ": %s",
204                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
205                  flags & GLINE_LOCAL ? "local" : "global",
206                  flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost,
207                  expire, reason);
208
209 #ifdef GPATH
210   /* and log it */
211   if (IsServer(sptr))
212     write_log(GPATH, "# " TIME_T_FMT " %s adding %s %s for %s, expiring at "
213               TIME_T_FMT ": %s\n", TStime(), sptr->name,
214               flags & GLINE_LOCAL ? "local" : "global",
215               flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire,
216               reason);
217   else
218     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s adding %s %s for %s, "
219               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
220               sptr->user->username, sptr->user->host,
221               flags & GLINE_LOCAL ? "local" : "global",
222               flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire,
223               reason);
224 #endif /* GPATH */
225
226   /* make the gline */
227   agline = make_gline(userhost, reason, expire, lastmod, flags);
228
229   propagate_gline(cptr, sptr, agline);
230
231   if (GlineIsBadChan(agline))
232     return 0;
233
234 #ifdef GPATH
235   /* this can be inserted into the conf */
236   write_log(GPATH, "%c:%s:%s:%s\n", GlineIsIpMask(agline) ? 'k' : 'K',
237             GlineHost(agline), GlineReason(agline), GlineUser(agline));
238 #endif /* GPATH */
239
240   return do_gline(cptr, sptr, agline); /* knock off users if necessary */
241 }
242
243 int
244 gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
245                time_t lastmod)
246 {
247   assert(0 != gline);
248
249   gline->gl_flags |= GLINE_ACTIVE;
250   gline->gl_lastmod = lastmod;
251
252   /* Inform ops and log it */
253   sendto_op_mask(SNO_GLINE, "%s activating %s %s for %s%s%s, expiring at "
254                  TIME_T_FMT ": %s",
255                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
256                  GlineIsLocal(gline) ? "local" : "global",
257                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
258                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
259                  GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
260                  gline->gl_reason);
261
262 #ifdef GPATH
263   if (IsServer(sptr))
264     write_log(GPATH, "# " TIME_T_FMT " %s activating %s %s for %s%s%s, "
265               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
266               GlineIsLocal(gline) ? "local" : "global",
267               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
268               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
269               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
270               gline->gl_reason);
271   else
272     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s activating %s %s for "
273               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
274               sptr->user->username, sptr->user->host,
275               GlineIsLocal(gline) ? "local" : "global",
276               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
277               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
278               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
279               gline->gl_reason);
280 #endif /* GPATH */
281
282   propagate_gline(cptr, sptr, gline);
283
284   return GlineIsBadChan(gline) ? 0 : do_gline(cptr, sptr, gline);
285 }
286
287 int
288 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
289                  time_t lastmod)
290 {
291   assert(0 != gline);
292
293   gline->gl_flags &= ~GLINE_ACTIVE;
294   gline->gl_lastmod = lastmod;
295
296   /* Inform ops and log it */
297   sendto_op_mask(SNO_GLINE, "%s deactivating %s %s for %s%s%s, expiring at "
298                  TIME_T_FMT ": %s",
299                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
300                  GlineIsLocal(gline) ? "local" : "global",
301                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
302                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
303                  GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
304                  gline->gl_reason);
305
306 #ifdef GPATH
307   if (IsServer(sptr))
308     write_log(GPATH, "# " TIME_T_FMT " %s deactivating %s %s for %s%s%s, "
309               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
310               GlineIsLocal(gline) ? "local" : "global",
311               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
312               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
313               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
314               gline->gl_reason);
315   else
316     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s deactivating %s %s for "
317               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
318               sptr->user->username, sptr->user->host,
319               GlineIsLocal(gline) ? "local" : "global",
320               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
321               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
322               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
323               gline->gl_reason);
324 #endif /* GPATH */
325
326   propagate_gline(cptr, sptr, gline);
327
328   return 0;
329 }
330
331 struct Gline *
332 gline_find(char *userhost, unsigned int flags)
333 {
334   struct Gline *gline;
335   struct Gline *sgline;
336   char *user, *host, *t_uh;
337
338   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
339     for (gline = BadChanGlineList; gline; gline = sgline) {
340       sgline = gline->gl_next;
341
342       if (gline->gl_expire <= TStime())
343         gline_free(gline);
344       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
345                 match(gline->gl_user, userhost)) == 0)
346         return gline;
347     }
348   }
349
350   if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN)
351     return 0;
352
353   DupString(t_uh, userhost);
354   canon_userhost(t_uh, &user, &host, 0);
355
356   for (gline = GlobalGlineList; gline; gline = sgline) {
357     sgline = gline->gl_next;
358
359     if (gline->gl_expire <= TStime())
360       gline_free(gline);
361     else if (flags & GLINE_EXACT) {
362       if (ircd_strcmp(gline->gl_host, host) == 0 &&
363           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
364            ircd_strcmp(gline->gl_user, user) == 0))
365         break;
366     } else {
367       if (match(gline->gl_host, host) == 0 &&
368           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
369            match(gline->gl_user, user) == 0))
370       break;
371     }
372   }
373
374   MyFree(t_uh);
375
376   return gline;
377 }
378
379 struct Gline *
380 gline_lookup(struct Client *cptr)
381 {
382   struct Gline *gline;
383   struct Gline *sgline;
384
385   for (gline = GlobalGlineList; gline; gline = sgline) {
386     sgline = gline->gl_next;
387
388     if (gline->gl_expire <= TStime())
389       gline_free(gline);
390     else if ((GlineIsIpMask(gline) ?
391               match(gline->gl_host, ircd_ntoa((const char *)&cptr->ip)) :
392               match(gline->gl_host, cptr->user->host)) == 0 &&
393              match(gline->gl_user, cptr->user->username) == 0)
394       return gline;
395   }
396
397   return 0;
398 }
399
400 void
401 gline_free(struct Gline *gline)
402 {
403   assert(0 != gline);
404
405   *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
406   if (gline->gl_next)
407     gline->gl_next->gl_prev_p = gline->gl_prev_p;
408
409   MyFree(gline->gl_user); /* free up the memory */
410   if (gline->gl_host)
411     MyFree(gline->gl_host);
412   MyFree(gline->gl_reason);
413   MyFree(gline);
414 }
415
416 void
417 gline_burst(struct Client *cptr)
418 {
419   struct Gline *gline;
420   struct Gline *sgline;
421
422   for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */
423     sgline = gline->gl_next;
424
425     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
426       gline_free(gline);
427     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
428       sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s@%s %Tu %Tu :%s",
429                     GlineIsActive(gline) ? '+' : '-', gline->gl_user,
430                     gline->gl_host, gline->gl_expire - TStime(),
431                     gline->gl_lastmod, gline->gl_reason);
432   }
433
434   for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */
435     sgline = gline->gl_next;
436
437     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
438       gline_free(gline);
439     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
440       sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s %Tu %Tu :%s",
441                     GlineIsActive(gline) ? '+' : '-', gline->gl_user,
442                     gline->gl_expire - TStime(), gline->gl_lastmod,
443                     gline->gl_reason);
444   }
445 }
446
447 int
448 gline_resend(struct Client *cptr, struct Gline *gline)
449 {
450   if (GlineIsLocal(gline) || !gline->gl_lastmod)
451     return 0;
452
453   sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s%s%s %Tu %Tu :%s",
454                 GlineIsActive(gline) ? '+' : '-', gline->gl_user,
455                 GlineIsBadChan(gline) ? "" : "@",
456                 GlineIsBadChan(gline) ? "" : gline->gl_host,
457                 gline->gl_expire - TStime(), gline->gl_lastmod,
458                 gline->gl_reason);
459
460   return 0;
461 }
462
463 int
464 gline_list(struct Client *sptr, char *userhost)
465 {
466   struct Gline *gline;
467   struct Gline *sgline;
468
469   if (userhost) {
470     if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
471       return send_error_to_client(sptr, ERR_NOSUCHGLINE, userhost);
472
473     /* send gline information along */
474     sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name, gline->gl_user,
475                GlineIsBadChan(gline) ? "" : "@",
476                GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
477                GlineIsLocal(gline) ? me.name : "*",
478                GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
479   } else {
480     for (gline = GlobalGlineList; gline; gline = sgline) {
481       sgline = gline->gl_next;
482
483       if (gline->gl_expire <= TStime())
484         gline_free(gline);
485       else
486         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
487                    gline->gl_user, "@", gline->gl_host, gline->gl_expire,
488                    GlineIsLocal(gline) ? me.name : "*",
489                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
490     }
491
492     for (gline = BadChanGlineList; gline; gline = sgline) {
493       sgline = gline->gl_next;
494
495       if (gline->gl_expire <= TStime())
496         gline_free(gline);
497       else
498         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
499                    gline->gl_user, "", "", gline->gl_expire,
500                    GlineIsLocal(gline) ? me.name : "*",
501                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
502     }
503   }
504
505   /* end of gline information */
506   sendto_one(sptr, rpl_str(RPL_ENDOFGLIST), me.name, sptr->name);
507   return 0;
508 }
509
510 void
511 gline_stats(struct Client *sptr)
512 {
513   struct Gline *gline;
514   struct Gline *sgline;
515
516   for (gline = GlobalGlineList; gline; gline = sgline) {
517     sgline = gline->gl_next;
518
519     if (gline->gl_expire <= TStime())
520       gline_free(gline);
521     else
522       sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, sptr->name, 'G',
523                  gline->gl_user, gline->gl_host, gline->gl_expire,
524                  gline->gl_reason);
525   }
526 }