Author: Kev <klmitch@mit.edu>
[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)) /* don't propagate local glines */
148     return;
149
150   if (IsUser(sptr)) { /* select appropriate source */
151     if (!gline->gl_lastmod)
152       return;
153     sendto_serv_butone(cptr, "%s%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT " "
154                        TIME_T_FMT " :%s", NumNick(sptr),
155                        GlineIsActive(gline) ? '+' : '-', gline->gl_user,
156                        GlineIsBadChan(gline) ? "" : "@",
157                        GlineIsBadChan(gline) ? "" : gline->gl_host,
158                        gline->gl_expire - TStime(), gline->gl_lastmod,
159                        gline->gl_reason);
160   } else {
161     if (gline->gl_lastmod)
162       sendto_serv_butone(cptr, "%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT " "
163                          TIME_T_FMT " :%s", NumServ(sptr),
164                          GlineIsActive(gline) ? '+' : '-', gline->gl_user,
165                          GlineIsBadChan(gline) ? "" : "@",
166                          GlineIsBadChan(gline) ? "" : gline->gl_host,
167                          gline->gl_expire - TStime(), gline->gl_lastmod,
168                          gline->gl_reason);
169     else
170       sendto_serv_butone(cptr, "%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT
171                          " :%s", NumServ(sptr),
172                          GlineIsActive(gline) ? '+' : '-', gline->gl_user,
173                          GlineIsBadChan(gline) ? "" : "@",
174                          GlineIsBadChan(gline) ? "" : gline->gl_host,
175                          gline->gl_expire - TStime(), gline->gl_reason);
176   }
177 }
178
179 int 
180 gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
181           char *reason, time_t expire, time_t lastmod, unsigned int flags)
182 {
183   struct Gline *agline;
184
185   assert(0 != userhost);
186   assert(0 != reason);
187
188   /*
189    * You cannot set a negative (or zero) expire time, nor can you set an
190    * expiration time for greater than GLINE_MAX_EXPIRE.
191    */
192   if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) {
193     if (!IsServer(sptr) && MyConnect(sptr))
194       send_error_to_client(sptr, ERR_BADEXPIRE, expire);
195     return 0;
196   }
197
198   expire += TStime(); /* convert from lifetime to timestamp */
199
200   /* NO_OLD_GLINE allows *@#channel to work correctly */
201 #ifdef BADCHAN
202   if (*userhost == '#' || *userhost == '&' || *userhost == '+'
203 # ifndef NO_OLD_GLINE
204       || userhost[2] == '#' || userhost[2] == '&' || userhost[2] == '+'
205 # endif /* OLD_GLINE */
206       ) {
207 # ifndef LOCAL_BADCHAN
208     if (flags & GLINE_LOCAL)
209       return 0;
210 # endif
211     flags |= GLINE_BADCHAN;
212   }
213 #endif /* BADCHAN */
214
215   /* Inform ops... */
216   sendto_op_mask(SNO_GLINE, "%s adding %s %s for %s, expiring at "
217                  TIME_T_FMT ": %s",
218                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
219                  flags & GLINE_LOCAL ? "local" : "global",
220                  flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost,
221                  expire, reason);
222
223 #ifdef GPATH
224   /* and log it */
225   if (IsServer(sptr))
226     write_log(GPATH, "# " TIME_T_FMT " %s adding %s %s for %s, expiring at "
227               TIME_T_FMT ": %s\n", TStime(), sptr->name,
228               flags & GLINE_LOCAL ? "local" : "global",
229               flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire,
230               reason);
231   else
232     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s adding %s %s for %s, "
233               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
234               sptr->user->username, sptr->user->host,
235               flags & GLINE_LOCAL ? "local" : "global",
236               flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire,
237               reason);
238 #endif /* GPATH */
239
240   /* make the gline */
241   agline = make_gline(userhost, reason, expire, lastmod, flags);
242
243   propagate_gline(cptr, sptr, agline);
244
245   if (GlineIsBadChan(agline))
246     return 0;
247
248 #ifdef GPATH
249   /* this can be inserted into the conf */
250   write_log(GPATH, "%c:%s:%s:%s\n", GlineIsIpMask(agline) ? 'k' : 'K',
251             GlineHost(agline), GlineReason(agline), GlineUser(agline));
252 #endif /* GPATH */
253
254   return do_gline(cptr, sptr, agline); /* knock off users if necessary */
255 }
256
257 int
258 gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
259                time_t lastmod)
260 {
261   assert(0 != gline);
262
263   gline->gl_flags |= GLINE_ACTIVE;
264   gline->gl_lastmod = lastmod;
265
266   /* Inform ops and log it */
267   sendto_op_mask(SNO_GLINE, "%s activating %s %s for %s%s%s, expiring at "
268                  TIME_T_FMT ": %s",
269                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
270                  GlineIsLocal(gline) ? "local" : "global",
271                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
272                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
273                  GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
274                  gline->gl_reason);
275
276 #ifdef GPATH
277   if (IsServer(sptr))
278     write_log(GPATH, "# " TIME_T_FMT " %s activating %s %s for %s%s%s, "
279               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
280               GlineIsLocal(gline) ? "local" : "global",
281               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
282               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
283               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
284               gline->gl_reason);
285   else
286     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s activating %s %s for "
287               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
288               sptr->user->username, sptr->user->host,
289               GlineIsLocal(gline) ? "local" : "global",
290               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
291               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
292               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
293               gline->gl_reason);
294 #endif /* GPATH */
295
296   propagate_gline(cptr, sptr, gline);
297
298   return GlineIsBadChan(gline) ? 0 : do_gline(cptr, sptr, gline);
299 }
300
301 int
302 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
303                  time_t lastmod)
304 {
305   assert(0 != gline);
306
307   gline->gl_flags &= ~GLINE_ACTIVE;
308   gline->gl_lastmod = lastmod;
309
310   /* Inform ops and log it */
311   sendto_op_mask(SNO_GLINE, "%s deactivating %s %s for %s%s%s, expiring at "
312                  TIME_T_FMT ": %s",
313                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
314                  GlineIsLocal(gline) ? "local" : "global",
315                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
316                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
317                  GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
318                  gline->gl_reason);
319
320 #ifdef GPATH
321   if (IsServer(sptr))
322     write_log(GPATH, "# " TIME_T_FMT " %s deactivating %s %s for %s%s%s, "
323               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
324               GlineIsLocal(gline) ? "local" : "global",
325               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
326               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
327               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
328               gline->gl_reason);
329   else
330     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s deactivating %s %s for "
331               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
332               sptr->user->username, sptr->user->host,
333               GlineIsLocal(gline) ? "local" : "global",
334               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
335               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
336               GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
337               gline->gl_reason);
338 #endif /* GPATH */
339
340   propagate_gline(cptr, sptr, gline);
341
342   return 0;
343 }
344
345 struct Gline *
346 gline_find(char *userhost, unsigned int flags)
347 {
348   struct Gline *gline;
349   struct Gline *sgline;
350   char *user, *host, *t_uh;
351
352   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
353     for (gline = BadChanGlineList; gline; gline = sgline) {
354       sgline = gline->gl_next;
355
356       if (gline->gl_expire <= TStime())
357         gline_free(gline);
358       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
359                 match(gline->gl_user, userhost)) == 0)
360         return gline;
361     }
362   }
363
364   if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN)
365     return 0;
366
367   DupString(t_uh, userhost);
368   canon_userhost(t_uh, &user, &host, 0);
369
370   for (gline = GlobalGlineList; gline; gline = sgline) {
371     sgline = gline->gl_next;
372
373     if (gline->gl_expire <= TStime())
374       gline_free(gline);
375     else if (flags & GLINE_EXACT) {
376       if (ircd_strcmp(gline->gl_host, host) == 0 &&
377           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
378            ircd_strcmp(gline->gl_user, user) == 0))
379         break;
380     } else {
381       if (match(gline->gl_host, host) == 0 &&
382           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
383            match(gline->gl_user, user) == 0))
384       break;
385     }
386   }
387
388   MyFree(t_uh);
389
390   return gline;
391 }
392
393 struct Gline *
394 gline_lookup(struct Client *cptr)
395 {
396   struct Gline *gline;
397   struct Gline *sgline;
398
399   for (gline = GlobalGlineList; gline; gline = sgline) {
400     sgline = gline->gl_next;
401
402     if (gline->gl_expire <= TStime())
403       gline_free(gline);
404     else if ((GlineIsIpMask(gline) ?
405               match(gline->gl_host, cptr->sock_ip) :
406               match(gline->gl_host, cptr->sockhost)) == 0 &&
407              match(gline->gl_user, cptr->user->username) == 0)
408       return gline;
409   }
410
411   return 0;
412 }
413
414 void
415 gline_free(struct Gline *gline)
416 {
417   assert(0 != gline);
418
419   *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
420   if (gline->gl_next)
421     gline->gl_next->gl_prev_p = gline->gl_prev_p;
422
423   MyFree(gline->gl_user); /* free up the memory */
424   if (gline->gl_host)
425     MyFree(gline->gl_host);
426   MyFree(gline->gl_reason);
427   MyFree(gline);
428 }
429
430 void
431 gline_burst(struct Client *cptr)
432 {
433   struct Gline *gline;
434   struct Gline *sgline;
435
436   for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */
437     sgline = gline->gl_next;
438
439     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
440       gline_free(gline);
441     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
442       sendto_one(cptr, "%s " TOK_GLINE " * %c%s@%s " TIME_T_FMT " " TIME_T_FMT
443                  " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-',
444                  gline->gl_user, gline->gl_host, gline->gl_expire - TStime(),
445                  gline->gl_lastmod, gline->gl_reason);
446   }
447
448   for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */
449     sgline = gline->gl_next;
450
451     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
452       gline_free(gline);
453     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
454       sendto_one(cptr, "%s " TOK_GLINE " * %c%s " TIME_T_FMT " " TIME_T_FMT
455                  " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-',
456                  gline->gl_user, gline->gl_expire - TStime(),
457                  gline->gl_lastmod, gline->gl_reason);
458   }
459 }
460
461 int
462 gline_resend(struct Client *cptr, struct Gline *gline)
463 {
464   if (GlineIsLocal(gline) || !gline->gl_lastmod)
465     return 0;
466
467   sendto_one(cptr, "%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT " " TIME_T_FMT
468              " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-',
469              gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
470              GlineIsBadChan(gline) ? "" : gline->gl_host,
471              gline->gl_expire - TStime(), gline->gl_lastmod, gline->gl_reason);
472
473   return 0;
474 }
475
476 int
477 gline_list(struct Client *sptr, char *userhost)
478 {
479   struct Gline *gline;
480   struct Gline *sgline;
481
482   if (userhost) {
483     if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
484       return send_error_to_client(sptr, ERR_NOSUCHGLINE, userhost);
485
486     /* send gline information along */
487     sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name, gline->gl_user,
488                GlineIsBadChan(gline) ? "" : "@",
489                GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire,
490                GlineIsLocal(gline) ? me.name : "*",
491                GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
492   } else {
493     for (gline = GlobalGlineList; gline; gline = sgline) {
494       sgline = gline->gl_next;
495
496       if (gline->gl_expire <= TStime())
497         gline_free(gline);
498       else
499         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
500                    gline->gl_user, "@", gline->gl_host, gline->gl_expire,
501                    GlineIsLocal(gline) ? me.name : "*",
502                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
503     }
504
505     for (gline = BadChanGlineList; gline; gline = sgline) {
506       sgline = gline->gl_next;
507
508       if (gline->gl_expire <= TStime())
509         gline_free(gline);
510       else
511         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
512                    gline->gl_user, "", "", gline->gl_expire,
513                    GlineIsLocal(gline) ? me.name : "*",
514                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
515     }
516   }
517
518   /* end of gline information */
519   sendto_one(sptr, rpl_str(RPL_ENDOFGLIST), me.name, sptr->name);
520   return 0;
521 }
522
523 void
524 gline_stats(struct Client *sptr)
525 {
526   struct Gline *gline;
527   struct Gline *sgline;
528
529   for (gline = GlobalGlineList; gline; gline = sgline) {
530     sgline = gline->gl_next;
531
532     if (gline->gl_expire <= TStime())
533       gline_free(gline);
534     else
535       sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, sptr->name, 'G',
536                  gline->gl_user, gline->gl_host, gline->gl_expire,
537                  gline->gl_reason);
538   }
539 }