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) || (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 - CurrentTime, 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 - CurrentTime, 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 += CurrentTime; /* 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 + TSoffset, 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,
216               expire + TSoffset, 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,
223               expire + TSoffset, 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   assert(!GlineIsLocal(gline));
249
250   gline->gl_flags |= GLINE_ACTIVE;
251
252   if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */
253     gline->gl_lastmod++;
254   else
255     gline->gl_lastmod = lastmod;
256
257   /* Inform ops and log it */
258   sendto_op_mask(SNO_GLINE, "%s activating global %s for %s%s%s, expiring at "
259                  TIME_T_FMT ": %s",
260                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
261                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
262                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
263                  GlineIsBadChan(gline) ? "" : gline->gl_host,
264                  gline->gl_expire + TSoffset, gline->gl_reason);
265
266 #ifdef GPATH
267   if (IsServer(sptr))
268     write_log(GPATH, "# " TIME_T_FMT " %s activating global %s for %s%s%s, "
269               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
270               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
271               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
272               GlineIsBadChan(gline) ? "" : gline->gl_host,
273               gline->gl_expire + TSoffset, gline->gl_reason);
274   else
275     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s activating %s for "
276               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
277               sptr->user->username, sptr->user->host,
278               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
279               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
280               GlineIsBadChan(gline) ? "" : gline->gl_host,
281               gline->gl_expire + TSoffset, gline->gl_reason);
282 #endif /* GPATH */
283
284   propagate_gline(cptr, sptr, gline);
285
286   return GlineIsBadChan(gline) ? 0 : do_gline(cptr, sptr, gline);
287 }
288
289 int
290 gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
291                  time_t lastmod)
292 {
293   assert(0 != gline);
294
295   if (!GlineIsLocal(gline)) {
296     gline->gl_flags &= ~GLINE_ACTIVE;
297
298     if (gline->gl_lastmod >= lastmod)
299       gline->gl_lastmod++;
300     else
301       gline->gl_lastmod = lastmod;
302   }
303
304   /* Inform ops and log it */
305   sendto_op_mask(SNO_GLINE, "%s %s %s for %s%s%s, expiring at "
306                  TIME_T_FMT ": %s",
307                  IsServer(sptr) ? sptr->name : sptr->user->server->name,
308                  GlineIsLocal(gline) ? "removing local" :
309                  "deactivating global",
310                  GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
311                  gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
312                  GlineIsBadChan(gline) ? "" : gline->gl_host,
313                  gline->gl_expire + TSoffset, gline->gl_reason);
314
315 #ifdef GPATH
316   if (IsServer(sptr))
317     write_log(GPATH, "# " TIME_T_FMT " %s %s %s for %s%s%s, "
318               "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
319               GlineIsLocal(gline) ? "removing local" : "deactivating global",
320               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
321               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
322               GlineIsBadChan(gline) ? "" : gline->gl_host,
323               gline->gl_expire + TSoffset, gline->gl_reason);
324   else
325     write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s %s %s for "
326               "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name,
327               sptr->user->username, sptr->user->host,
328               GlineIsLocal(gline) ? "removing local" : "deactivating global",
329               GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
330               gline->gl_user, GlineIsBadChan(gline) ? "" : "@",
331               GlineIsBadChan(gline) ? "" : gline->gl_host,
332               gline->gl_expire + TSoffset, gline->gl_reason);
333 #endif /* GPATH */
334
335   if (GlineIsLocal(gline))
336     gline_free(gline);
337   else
338     propagate_gline(cptr, sptr, gline);
339
340   return 0;
341 }
342
343 struct Gline *
344 gline_find(char *userhost, unsigned int flags)
345 {
346   struct Gline *gline;
347   struct Gline *sgline;
348   char *user, *host, *t_uh;
349
350   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
351     for (gline = BadChanGlineList; gline; gline = sgline) {
352       sgline = gline->gl_next;
353
354       if (gline->gl_expire <= TStime())
355         gline_free(gline);
356       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
357                 match(gline->gl_user, userhost)) == 0)
358         return gline;
359     }
360   }
361
362   if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN ||
363       *userhost == '#' || *userhost == '&' || *userhost == '+'
364 #ifndef NO_OLD_GLINE
365       || userhost[2] == '#' || userhost[2] == '&' || userhost[2] == '+'
366 #endif /* NO_OLD_GLINE */
367       )
368     return 0;
369
370   DupString(t_uh, userhost);
371   canon_userhost(t_uh, &user, &host, 0);
372
373   for (gline = GlobalGlineList; gline; gline = sgline) {
374     sgline = gline->gl_next;
375
376     if (gline->gl_expire <= TStime())
377       gline_free(gline);
378     else if (flags & GLINE_EXACT) {
379       if (ircd_strcmp(gline->gl_host, host) == 0 &&
380           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
381            ircd_strcmp(gline->gl_user, user) == 0))
382         break;
383     } else {
384       if (match(gline->gl_host, host) == 0 &&
385           ((!user && ircd_strcmp(gline->gl_user, "*") == 0) ||
386            match(gline->gl_user, user) == 0))
387       break;
388     }
389   }
390
391   MyFree(t_uh);
392
393   return gline;
394 }
395
396 struct Gline *
397 gline_lookup(struct Client *cptr)
398 {
399   struct Gline *gline;
400   struct Gline *sgline;
401
402   for (gline = GlobalGlineList; gline; gline = sgline) {
403     sgline = gline->gl_next;
404
405     if (gline->gl_expire <= TStime())
406       gline_free(gline);
407     else if ((GlineIsIpMask(gline) ?
408               match(gline->gl_host, ircd_ntoa((const char *)&cptr->ip)) :
409               match(gline->gl_host, cptr->user->host)) == 0 &&
410              match(gline->gl_user, cptr->user->username) == 0)
411       return gline;
412   }
413
414   return 0;
415 }
416
417 void
418 gline_free(struct Gline *gline)
419 {
420   assert(0 != gline);
421
422   *gline->gl_prev_p = gline->gl_next; /* squeeze this gline out */
423   if (gline->gl_next)
424     gline->gl_next->gl_prev_p = gline->gl_prev_p;
425
426   MyFree(gline->gl_user); /* free up the memory */
427   if (gline->gl_host)
428     MyFree(gline->gl_host);
429   MyFree(gline->gl_reason);
430   MyFree(gline);
431 }
432
433 void
434 gline_burst(struct Client *cptr)
435 {
436   struct Gline *gline;
437   struct Gline *sgline;
438
439   for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */
440     sgline = gline->gl_next;
441
442     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
443       gline_free(gline);
444     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
445       sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s@%s %Tu %Tu :%s",
446                     GlineIsActive(gline) ? '+' : '-', gline->gl_user,
447                     gline->gl_host, gline->gl_expire - CurrentTime,
448                     gline->gl_lastmod, gline->gl_reason);
449   }
450
451   for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */
452     sgline = gline->gl_next;
453
454     if (gline->gl_expire <= TStime()) /* expire any that need expiring */
455       gline_free(gline);
456     else if (!GlineIsLocal(gline) && gline->gl_lastmod)
457       sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s %Tu %Tu :%s",
458                     GlineIsActive(gline) ? '+' : '-', gline->gl_user,
459                     gline->gl_expire - CurrentTime, gline->gl_lastmod,
460                     gline->gl_reason);
461   }
462 }
463
464 int
465 gline_resend(struct Client *cptr, struct Gline *gline)
466 {
467   if (GlineIsLocal(gline) || !gline->gl_lastmod)
468     return 0;
469
470   sendcmdto_one(cptr, CMD_GLINE, &me, "* %c%s%s%s %Tu %Tu :%s",
471                 GlineIsActive(gline) ? '+' : '-', gline->gl_user,
472                 GlineIsBadChan(gline) ? "" : "@",
473                 GlineIsBadChan(gline) ? "" : gline->gl_host,
474                 gline->gl_expire - CurrentTime, gline->gl_lastmod,
475                 gline->gl_reason);
476
477   return 0;
478 }
479
480 int
481 gline_list(struct Client *sptr, char *userhost)
482 {
483   struct Gline *gline;
484   struct Gline *sgline;
485
486   if (userhost) {
487     if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */
488       return send_error_to_client(sptr, ERR_NOSUCHGLINE, userhost);
489
490     /* send gline information along */
491     sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name, gline->gl_user,
492                GlineIsBadChan(gline) ? "" : "@",
493                GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire + TSoffset,
494                GlineIsLocal(gline) ? me.name : "*",
495                GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
496   } else {
497     for (gline = GlobalGlineList; gline; gline = sgline) {
498       sgline = gline->gl_next;
499
500       if (gline->gl_expire <= TStime())
501         gline_free(gline);
502       else
503         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
504                    gline->gl_user, "@", gline->gl_host, gline->gl_expire + TSoffset,
505                    GlineIsLocal(gline) ? me.name : "*",
506                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
507     }
508
509     for (gline = BadChanGlineList; gline; gline = sgline) {
510       sgline = gline->gl_next;
511
512       if (gline->gl_expire <= TStime())
513         gline_free(gline);
514       else
515         sendto_one(sptr, rpl_str(RPL_GLIST), me.name, sptr->name,
516                    gline->gl_user, "", "", gline->gl_expire + TSoffset,
517                    GlineIsLocal(gline) ? me.name : "*",
518                    GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
519     }
520   }
521
522   /* end of gline information */
523   sendto_one(sptr, rpl_str(RPL_ENDOFGLIST), me.name, sptr->name);
524   return 0;
525 }
526
527 void
528 gline_stats(struct Client *sptr)
529 {
530   struct Gline *gline;
531   struct Gline *sgline;
532
533   for (gline = GlobalGlineList; gline; gline = sgline) {
534     sgline = gline->gl_next;
535
536     if (gline->gl_expire <= TStime())
537       gline_free(gline);
538     else
539       sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, sptr->name, 'G',
540                  gline->gl_user, gline->gl_host, gline->gl_expire + TSoffset,
541                  gline->gl_reason);
542   }
543 }